maven-surefire-plugin
+ 2.18.1
always
diff --git a/mvvmfx-testing-utils/pom.xml b/mvvmfx-testing-utils/pom.xml
index 883a3dd80..e9ac21324 100644
--- a/mvvmfx-testing-utils/pom.xml
+++ b/mvvmfx-testing-utils/pom.xml
@@ -5,7 +5,7 @@
mvvmfx-parent
de.saxsys
- 1.1.0
+ 1.2.0
4.0.0
diff --git a/mvvmfx-utils/pom.xml b/mvvmfx-utils/pom.xml
index 0dba16603..2e610e4f7 100644
--- a/mvvmfx-utils/pom.xml
+++ b/mvvmfx-utils/pom.xml
@@ -5,7 +5,7 @@
mvvmfx-parent
de.saxsys
- 1.1.0
+ 1.2.0
4.0.0
diff --git a/mvvmfx/pom.xml b/mvvmfx/pom.xml
index 67508b864..f67c9800c 100644
--- a/mvvmfx/pom.xml
+++ b/mvvmfx/pom.xml
@@ -20,7 +20,7 @@
de.saxsys
mvvmfx-parent
- 1.1.0
+ 1.2.0
mvvmfx
@@ -83,6 +83,13 @@
test
+
+ com.cedarsoft.commons
+ test-utils
+ 6.1.1
+ test
+
+
diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/ViewModel.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/ViewModel.java
index 5c6d60260..83c137462 100644
--- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/ViewModel.java
+++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/ViewModel.java
@@ -15,12 +15,22 @@
******************************************************************************/
package de.saxsys.mvvmfx;
+import javafx.application.Platform;
import de.saxsys.mvvmfx.internal.viewloader.View;
import de.saxsys.mvvmfx.utils.notifications.NotificationCenter;
+import de.saxsys.mvvmfx.utils.notifications.NotificationObserver;
/**
*
- * Marker interface for a View Model. Some additional hints to this layer:
+ * Interface for a View Model.
+ *
+ *
+ * You can use a notification mechanism by using the {@link #publish(String, Object...)} method. In the View you can
+ * subscribe to this notifications by using viewModel.
+ * {@link #subscribe(String messageName, NotificationObserver observer)}.
+ *
+ *
+ * Some additional hints to this layer:
*
*
*
@@ -33,4 +43,55 @@
*
*/
public interface ViewModel {
+
+ /**
+ * Publishes a notification to the subscribers of the notificationId. This notification will be send to the
+ * UI-Thread.
+ *
+ * @param messageName
+ * of the notification
+ * @param payload
+ * to be send
+ */
+ default void publish(String messageName, Object... payload) {
+ if (!Platform.isFxApplicationThread()) {
+ MvvmFX.getNotificationCenter().publish(this, messageName, payload);
+ } else {
+ MvvmFX.getNotificationCenter().publish(this, messageName, payload);
+ }
+ }
+
+ /**
+ * Subscribe to a notification with a given {@link NotificationObserver}.
+ *
+ * @param messageName
+ * of the Notification
+ * @param observer
+ * which should execute when the notification occurs
+ */
+ default void subscribe(String messageName, NotificationObserver observer) {
+ MvvmFX.getNotificationCenter().subscribe(this, messageName, observer);
+ }
+
+ /**
+ * Remove the observer for a specific notification by a given messageName.
+ *
+ * @param messageName
+ * of the notification for that the observer should be removed
+ * @param observer
+ * to remove
+ */
+ default void unsubscribe(String messageName, NotificationObserver observer) {
+ MvvmFX.getNotificationCenter().unsubscribe(this, messageName, observer);
+ }
+
+ /**
+ * Removes the observer for all messages.
+ *
+ * @param observer
+ * to be removed
+ */
+ default void unsubscribe(NotificationObserver observer) {
+ MvvmFX.getNotificationCenter().unsubscribe(this, observer);
+ }
}
diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/Action.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/Action.java
new file mode 100644
index 000000000..797a5c43b
--- /dev/null
+++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/Action.java
@@ -0,0 +1,15 @@
+package de.saxsys.mvvmfx.utils.commands;
+
+import javafx.concurrent.Task;
+
+public abstract class Action extends Task {
+
+ @Override
+ protected Void call() throws Exception {
+ action();
+ return null;
+ }
+
+ protected abstract void action() throws Exception;
+
+}
diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/Command.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/Command.java
index 55b565ac5..31a58e292 100644
--- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/Command.java
+++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/Command.java
@@ -15,8 +15,9 @@
******************************************************************************/
package de.saxsys.mvvmfx.utils.commands;
-import eu.lestard.doc.Beta;
import javafx.beans.property.ReadOnlyBooleanProperty;
+import javafx.beans.property.ReadOnlyDoubleProperty;
+import eu.lestard.doc.Beta;
/**
* The {@link Command} encapsulates logic in the {@link #execute()} method which will be called later. This can be used
@@ -50,6 +51,18 @@ public interface Command {
*/
ReadOnlyBooleanProperty executableProperty();
+ /**
+ * Determines whether the command can not execute in it's current state.
+ *
+ * @return true
if the {@link Command} can not execute, otherwise false
.
+ */
+ boolean isNotExecutable();
+
+ /**
+ * @see #isNotExecutable()
+ */
+ ReadOnlyBooleanProperty notExecutableProperty();
+
/**
* Signals whether the command is currently executing. This can be useful especially for commands that are executed
@@ -64,4 +77,31 @@ public interface Command {
*/
ReadOnlyBooleanProperty runningProperty();
+ /**
+ * Signals whether the command is currently not executing. This can be useful especially for commands that are
+ * executed asynchronously.
+ *
+ * @return true
if the {@link Command} is not running, otherwise false
.
+ */
+ boolean isNotRunning();
+
+ /**
+ * @see #isNotRunning()
+ */
+ ReadOnlyBooleanProperty notRunningProperty();
+
+ /**
+ * Gets a double between 0.0 and 1.0 which represents the progress.
+ *
+ * @return progress
+ */
+ double getProgress();
+
+ /**
+ * @see #getProgress()
+ */
+ ReadOnlyDoubleProperty progressProperty();
+
+
+
}
\ No newline at end of file
diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/CommandBase.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/CommandBase.java
index 331af0f61..a81b04344 100644
--- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/CommandBase.java
+++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/CommandBase.java
@@ -32,24 +32,55 @@ public abstract class CommandBase implements Command {
protected final ReadOnlyBooleanWrapper executable = new ReadOnlyBooleanWrapper(true);
protected final ReadOnlyBooleanWrapper running = new ReadOnlyBooleanWrapper(false);
+ protected ReadOnlyBooleanWrapper notExecutable;
+ protected ReadOnlyBooleanWrapper notRunning;
+
+ @Override
+ public final ReadOnlyBooleanProperty executableProperty() {
+ return executable.getReadOnlyProperty();
+ }
+
+ @Override
+ public final boolean isExecutable() {
+ return executableProperty().get();
+ }
+
+ @Override
+ public final ReadOnlyBooleanProperty notExecutableProperty() {
+ if (notExecutable == null) {
+ notExecutable = new ReadOnlyBooleanWrapper();
+ notExecutable.bind(executableProperty().not());
+ }
+ return notExecutable.getReadOnlyProperty();
+ }
+
+ @Override
+ public final boolean isNotExecutable() {
+ return notExecutableProperty().get();
+ }
+
@Override
- public ReadOnlyBooleanProperty executableProperty() {
- return this.executable.getReadOnlyProperty();
+ public final ReadOnlyBooleanProperty runningProperty() {
+ return running.getReadOnlyProperty();
}
@Override
- public boolean isExecutable() {
- return this.executableProperty().get();
+ public final boolean isRunning() {
+ return running.get();
}
@Override
- public ReadOnlyBooleanProperty runningProperty() {
- return this.running.getReadOnlyProperty();
+ public final ReadOnlyBooleanProperty notRunningProperty() {
+ if (notRunning == null) {
+ notRunning = new ReadOnlyBooleanWrapper();
+ notRunning.bind(runningProperty().not());
+ }
+ return notRunning.getReadOnlyProperty();
}
@Override
- public boolean isRunning() {
- return this.running.get();
+ public final boolean isNotRunning() {
+ return notRunningProperty().get();
}
@Override
diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/CompositeCommand.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/CompositeCommand.java
index a36cd72ca..18ad8622a 100644
--- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/CompositeCommand.java
+++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/CompositeCommand.java
@@ -15,12 +15,21 @@
******************************************************************************/
package de.saxsys.mvvmfx.utils.commands;
-import eu.lestard.doc.Beta;
+import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
+import javafx.beans.binding.DoubleBinding;
+import javafx.beans.binding.DoubleExpression;
import javafx.beans.property.ReadOnlyBooleanProperty;
+import javafx.beans.property.ReadOnlyDoubleProperty;
+import javafx.beans.property.ReadOnlyDoubleWrapper;
+import javafx.beans.value.ObservableDoubleValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
+import eu.lestard.doc.Beta;
+
+import java.util.concurrent.Callable;
+import java.util.function.Function;
/**
* CompositeCommand is an aggregation of other commands - a list of {@link Command} references internally.
@@ -48,6 +57,8 @@ public class CompositeCommand extends CommandBase {
private final ObservableList registeredCommands = FXCollections.observableArrayList();
+ ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper();
+
/**
* Creates a {@link CompositeCommand} with given commands.
*
@@ -83,39 +94,89 @@ public void unregister(Command command) {
private void initRegisteredCommandsListener() {
this.registeredCommands.addListener((ListChangeListener) c -> {
while (c.next()) {
- if(registeredCommands.isEmpty()) {
+ if (registeredCommands.isEmpty()) {
executable.unbind();
running.unbind();
+ progress.unbind();
} else {
- BooleanBinding executableBinding = null;
- BooleanBinding runningBinding = null;
+ BooleanBinding executableBinding = constantOf(true);
+ BooleanBinding runningBinding = constantOf(false);
- for (int i = 0; i < registeredCommands.size(); i++) {
- ReadOnlyBooleanProperty currentExecutable = registeredCommands.get(i).executableProperty();
- ReadOnlyBooleanProperty currentRunning = registeredCommands.get(i).runningProperty();
-
- if (i == 0) {
- executableBinding = currentExecutable.and(currentExecutable);
- runningBinding = currentRunning.or(currentRunning);
- } else {
- executableBinding = executableBinding.and(currentExecutable);
- runningBinding = runningBinding.or(currentRunning);
- }
+ for (Command registeredCommand : registeredCommands) {
+ ReadOnlyBooleanProperty currentExecutable = registeredCommand.executableProperty();
+ ReadOnlyBooleanProperty currentRunning = registeredCommand.runningProperty();
+ executableBinding = executableBinding.and(currentExecutable);
+ runningBinding = runningBinding.or(currentRunning);
}
executable.bind(executableBinding);
running.bind(runningBinding);
+
+ initProgressBinding();
}
}
});
}
+ private void initProgressBinding() {
+ DoubleExpression tmp = constantOf(0);
+
+ for (Command command : registeredCommands) {
+ final ReadOnlyDoubleProperty progressProperty = command.progressProperty();
+
+ /**
+ * When the progress of a command is "undefined", the progress property has a value of -1.
+ * But in our use case we like to have a value of 0 in this case.
+ * Therefore we create a custom binding here.
+ */
+ final DoubleBinding normalizedProgress = Bindings
+ .createDoubleBinding(() -> (progressProperty.get() == -1) ? 0.0 : progressProperty.get(),
+ progressProperty);
+
+ tmp = tmp.add(normalizedProgress);
+ }
+
+ int divisor = registeredCommands.isEmpty() ? 1 : registeredCommands.size();
+ progress.bind(Bindings.divide(tmp, divisor));
+ }
+
@Override
public void execute() {
if (!isExecutable()) {
throw new RuntimeException("Not executable");
} else {
- registeredCommands.forEach(t -> t.execute());
+ if (!registeredCommands.isEmpty()) {
+ registeredCommands.forEach(t -> t.execute());
+ }
}
}
+
+ @Override
+ public double getProgress() {
+ return progressProperty().get();
+ }
+
+ @Override
+ public ReadOnlyDoubleProperty progressProperty() {
+ return progress;
+ }
+
+ private BooleanBinding constantOf(boolean defaultValue) {
+ return new BooleanBinding() {
+ @Override
+ protected boolean computeValue() {
+ return defaultValue;
+ }
+ };
+ }
+
+ private DoubleBinding constantOf(double defaultValue) {
+ return new DoubleBinding() {
+ @Override
+ protected double computeValue() {
+ return defaultValue;
+ }
+ };
+ }
+
}
diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/DelegateCommand.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/DelegateCommand.java
index 82210e9b6..ac476b06e 100644
--- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/DelegateCommand.java
+++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/commands/DelegateCommand.java
@@ -15,32 +15,45 @@
******************************************************************************/
package de.saxsys.mvvmfx.utils.commands;
+import java.util.function.Supplier;
+
import javafx.application.Platform;
+import javafx.beans.property.ReadOnlyBooleanProperty;
+import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.value.ObservableBooleanValue;
+import javafx.concurrent.Service;
+import javafx.concurrent.Task;
import eu.lestard.doc.Beta;
/**
- * A {@link Command} implementation that encapsulates an action ({@link Runnable}). It is possible to define that the
- * action should be executed in the background (not on the JavaFX thread) so that long running actions can be
- * implemented that aren't blocking the ui thread.
+ * A {@link Command} implementation of a {@link Service} that encapsulates an {@link Action} ({@link Task})
+ * which can be called from the UI - for example after a button click. If the {@link Action} is a long running
+ * operation, which would block your UI, you can pass a parameter to perform the {@link Action} in a background thread.
+ * You can bind to the {@link #isRunning()} property while the action is executing. This can be used for a loading
+ * indication in the UI.
*
* @author alexander.casall
*/
@Beta
-public class DelegateCommand extends CommandBase {
+public class DelegateCommand extends Service implements Command {
- private final Runnable action;
+ private final Supplier actionSupplier;
private boolean inBackground = false;
+ protected final ReadOnlyBooleanWrapper executable = new ReadOnlyBooleanWrapper(true);
+ protected ReadOnlyBooleanWrapper notExecutable;
+ protected ReadOnlyBooleanWrapper notRunning;
+
+
+
/**
- * Creates a command without a condition about the executability. The command will perform in the thread which
- * executes the {@link Command}.
+ * Creates a command without a condition about the executability.
*
- * @param action
- * which should execute
+ * @param actionSupplier
+ * a function that returns a new Action which should be executed
*/
- public DelegateCommand(Runnable action) {
- this(action, null, false);
+ public DelegateCommand(final Supplier actionSupplier) {
+ this(actionSupplier, false);
}
/**
@@ -49,28 +62,27 @@ public DelegateCommand(Runnable action) {
*
* IF YOU USE THE BACKGROUND THREAD: Your provided action will perform in a background thread. If you
* manipulate data in your action, which will be propagated to the UI, use {@link Platform#runLater(Runnable)} for
- * this manipulation, otherwise you get an Exception.
- *
- * @param action
- * which should execute
+ * this manipulation, otherwise you get an Exception by JavaFX.
+ *
+ * @param actionSupplier
+ * a function that returns a new Action which should be executed
* @param inBackground
* defines whether the execution {@link #execute()} is performed in a background thread or not
*/
- public DelegateCommand(Runnable action, boolean inBackground) {
- this(action, null, inBackground);
+ public DelegateCommand(final Supplier actionSupplier, boolean inBackground) {
+ this(actionSupplier, null, inBackground);
}
/**
- * Creates a command with a condition about the executability by using the #executableBinding parameter. The command
- * will perform in the thread which executes the {@link Command}.
- *
- * @param action
- * which should execute
+ * Creates a command with a condition about the executability by using the #executableBinding parameter.
+ *
+ * @param actionSupplier
+ * a function that returns a new Action which should be executed
* @param executableBinding
* which defines whether the {@link Command} can execute
*/
- public DelegateCommand(Runnable action, ObservableBooleanValue executableBinding) {
- this(action, executableBinding, false);
+ public DelegateCommand(final Supplier actionSupplier, ObservableBooleanValue executableBinding) {
+ this(actionSupplier, executableBinding, false);
}
/**
@@ -79,20 +91,22 @@ public DelegateCommand(Runnable action, ObservableBooleanValue executableBinding
*
* IF YOU USE THE BACKGROUND THREAD: don't forget to return to the UI-thread by using
* {@link Platform#runLater(Runnable)}, otherwise you get an Exception.
- *
- * @param action
- * which should execute
+ *
+ * @param actionSupplier
+ * a function that returns a new Action which should be executed
* @param executableBinding
* which defines whether the {@link Command} can execute
* @param inBackground
* defines whether the execution {@link #execute()} is performed in a background thread or not
*/
- public DelegateCommand(Runnable action, ObservableBooleanValue executableBinding, boolean inBackground) {
- this.action = action;
+ public DelegateCommand(final Supplier actionSupplier, ObservableBooleanValue executableBinding,
+ boolean inBackground) {
+ this.actionSupplier = actionSupplier;
this.inBackground = inBackground;
if (executableBinding != null) {
executable.bind(runningProperty().not().and(executableBinding));
}
+
}
/**
@@ -100,27 +114,65 @@ public DelegateCommand(Runnable action, ObservableBooleanValue executableBinding
*/
@Override
public void execute() {
-
- final boolean callerOnUIThread = Platform.isFxApplicationThread();
-
if (!isExecutable()) {
throw new RuntimeException("The execute()-method of the command was called while it wasn't executable.");
} else {
- running.set(true);
if (inBackground) {
- new Thread(() -> {
- action.run();
- if (callerOnUIThread) {
- Platform.runLater(() -> running.set(false));
- } else {
- running.set(false);
- }
- }).start();
+ if (!super.isRunning()) {
+ reset();
+ start();
+ }
} else {
- action.run();
- running.set(false);
+ try {
+ actionSupplier.get().action();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
}
}
}
+ @Override
+ protected Task createTask() {
+ return actionSupplier.get();
+ }
+
+ @Override
+ public ReadOnlyBooleanProperty executableProperty() {
+ return this.executable.getReadOnlyProperty();
+ }
+
+
+ @Override
+ public boolean isExecutable() {
+ return this.executableProperty().get();
+ }
+
+ @Override
+ public final ReadOnlyBooleanProperty notExecutableProperty() {
+ if (notExecutable == null) {
+ notExecutable = new ReadOnlyBooleanWrapper();
+ notExecutable.bind(executableProperty().not());
+ }
+ return notExecutable.getReadOnlyProperty();
+ }
+
+ @Override
+ public final boolean isNotExecutable() {
+ return notExecutableProperty().get();
+ }
+
+ @Override
+ public final ReadOnlyBooleanProperty notRunningProperty() {
+ if (notRunning == null) {
+ notRunning = new ReadOnlyBooleanWrapper();
+ notRunning.bind(runningProperty().not());
+ }
+ return notRunning.getReadOnlyProperty();
+ }
+
+ @Override
+ public final boolean isNotRunning() {
+ return notRunningProperty().get();
+ }
}
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 a42a62db3..e7c5e045f 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
@@ -1,10 +1,28 @@
package de.saxsys.mvvmfx.utils.mapping;
+import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.BooleanGetter;
+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.DoublePropertyAccessor;
+import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.DoubleSetter;
+import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.FloatGetter;
+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.IntPropertyAccessor;
+import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.IntSetter;
+import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.LongGetter;
+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.ObjectPropertyAccessor;
+import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ObjectSetter;
+import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.StringGetter;
+import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.StringPropertyAccessor;
+import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.StringSetter;
import eu.lestard.doc.Beta;
-import javafx.beans.property.ObjectProperty;
-import javafx.beans.property.Property;
-import javafx.beans.property.SimpleObjectProperty;
-import javafx.beans.value.WritableValue;
+import javafx.beans.property.*;
import java.util.HashMap;
import java.util.HashSet;
@@ -12,6 +30,7 @@
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;
+import java.util.function.Supplier;
/**
@@ -158,15 +177,15 @@
* }
* }
*
- * public Property{@code} nameProperty(){
+ * public StringProperty nameProperty(){
* return wrapper.field("name", Person::getName, Person::setName, "");
* }
*
- * public Property{@code} familyNameProperty(){
+ * public StringProperty familyNameProperty(){
* return wrapper.field("familyName", Person::getFamilyName, Person::setFamilyName, "");
* }
*
- * public Property{@code} ageProperty() {
+ * public IntegerProperty ageProperty() {
* return wrapper.field("age", Person::getAge, Person::setAge, 0);
* }
* }
@@ -181,6 +200,10 @@
* ViewModel that would need an update when the structure of the model changes.
*
*
+ *
+ *
+ *
+ *
* @param
* the type of the model class.
*/
@@ -193,14 +216,14 @@ public class ModelWrapper {
* @param
* @param
*/
- private interface PropertyField {
+ private interface PropertyField> {
void commit(M wrappedObject);
void reload(M wrappedObject);
void resetToDefault();
- Property getProperty();
+ R getProperty();
}
/**
@@ -209,25 +232,26 @@ private interface PropertyField {
*
* @param
*/
- private class FxPropertyField implements PropertyField {
+ private class FxPropertyField> implements PropertyField {
private final T defaultValue;
- private final Function> accessor;
- private final ObjectProperty targetProperty;
+ private final Function> accessor;
+ private final R targetProperty;
- public FxPropertyField(Function> accessor) {
- this(accessor, null);
+ public FxPropertyField(Function> accessor, Supplier> propertySupplier) {
+ this(accessor, null, propertySupplier);
}
- public FxPropertyField(Function> accessor, T defaultValue) {
+ @SuppressWarnings("unchecked")
+ public FxPropertyField(Function> accessor, T defaultValue, Supplier> propertySupplier) {
this.accessor = accessor;
this.defaultValue = defaultValue;
- this.targetProperty = new SimpleObjectProperty<>();
+ this.targetProperty = (R) propertySupplier.get();
}
@Override
public void commit(M wrappedObject) {
- accessor.apply(wrappedObject).setValue(targetProperty.get());
+ accessor.apply(wrappedObject).setValue(targetProperty.getValue());
}
@Override
@@ -241,7 +265,7 @@ public void resetToDefault() {
}
@Override
- public Property getProperty() {
+ public R getProperty() {
return targetProperty;
}
}
@@ -252,30 +276,30 @@ public Property getProperty() {
*
* @param
*/
- private class BeanPropertyField implements PropertyField {
+ private class BeanPropertyField> implements PropertyField {
- private final ObjectProperty targetProperty;
+ private final R targetProperty;
private final T defaultValue;
private final Function getter;
private final BiConsumer setter;
public BeanPropertyField(Function getter,
- BiConsumer setter) {
- this(getter, setter, null);
+ BiConsumer setter, Supplier propertySupplier) {
+ this(getter, setter, null, propertySupplier);
}
public BeanPropertyField(Function getter,
- BiConsumer setter, T defaultValue) {
+ BiConsumer setter, T defaultValue, Supplier propertySupplier) {
this.defaultValue = defaultValue;
this.getter = getter;
this.setter = setter;
- this.targetProperty = new SimpleObjectProperty<>();
+ this.targetProperty = propertySupplier.get();
}
@Override
public void commit(M wrappedObject) {
- setter.accept(wrappedObject, targetProperty.get());
+ setter.accept(wrappedObject, targetProperty.getValue());
}
@Override
@@ -289,14 +313,14 @@ public void resetToDefault() {
}
@Override
- public Property getProperty() {
+ public R getProperty() {
return targetProperty;
}
}
- private Set> fields = new HashSet<>();
- private Map> identifiedFields = new HashMap<>();
+ private Set> fields = new HashSet<>();
+ private Map> identifiedFields = new HashMap<>();
private M model;
@@ -375,90 +399,28 @@ public void reload() {
}
}
- /**
- * Add a new field 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.
- *
- * Example:
- *
- *
- *
- * ModelWrapper{@code} personWrapper = new ModelWrapper{@code<>}();
- *
- * Property{@code} wrappedNameProperty = personWrapper.field(person -> person.nameProperty());
- *
- * // or with a method reference
- * Property{@code} wrappedNameProperty = personWrapper.field(Person::nameProperty);
- *
- *
- *
- *
- * @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.
- *
- * @param
- * the type of the field.
- *
- * @return The wrapped property instance.
- */
- public Property field(Function> accessor) {
- return add(new FxPropertyField<>(accessor));
- }
- /**
- * Add a new field 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.
- *
- * Additionally you can define a default value that is used as when {@link #reset()} is invoked.
- *
- *
- *
- * Example:
- *
- *
- *
- * ModelWrapper{@code} personWrapper = new ModelWrapper{@code<>}();
- *
- * Property{@code} wrappedNameProperty = personWrapper.field(person -> person.nameProperty(), "empty");
- *
- * // or with a method reference
- * Property{@code} wrappedNameProperty = personWrapper.field(Person::nameProperty, "empty");
- *
- *
- *
- *
- * @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.
- * @param defaultValue
- * the default value for the field.
- * @param
- * the type of the field.
- *
- * @return The wrapped property instance.
- */
- public Property field(Function> accessor, T defaultValue) {
- return add(new FxPropertyField<>(accessor, defaultValue));
- }
+
+ /** Field type String **/
/**
- * Add a new field 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 and not as
- * JavaFX Properties.
+ * 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
+ * and not as JavaFX Properties.
*
*
*
* Example:
*
- *
+ *
*
- * ModelWrapper{@code} personWrapper = new ModelWrapper{@code<>}();
+ * ModelWrapper{@code} personWrapper = new ModelWrapper{@code<>}();
*
- * Property{@code} wrappedNameProperty = personWrapper.field(person -> person.getName(), (person, value) -> person.setName(value), "empty");
+ * StringProperty wrappedNameProperty = personWrapper.field(person -> person.getName(), (person, value)
+ * -> person.setName(value), "empty");
*
- * // or with a method reference
- * Property{@code} wrappedNameProperty = personWrapper.field(Person::getName, Person::setName, "empty");
+ * // or with a method reference
+ * StringProperty wrappedNameProperty = personWrapper.field(Person::getName, Person::setName, "empty");
*
*
*
@@ -469,36 +431,17 @@ public Property field(Function> accessor, T defaultVa
* @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.
- * @param
- * the type of the field.
*
* @return The wrapped property instance.
*/
- public Property field(Function getter, BiConsumer setter) {
- return add(new BeanPropertyField<>(getter, setter));
+ public StringProperty field(StringGetter getter, StringSetter setter) {
+ return add(new BeanPropertyField<>(getter, setter, SimpleStringProperty::new));
}
/**
- * Add a new field 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 and not as
- * JavaFX Properties.
- *
- * Additionally you can define a default value that is used as when {@link #reset()} is invoked.
- *
- *
- *
- * Example:
- *
- *
- *
- * ModelWrapper{@code} personWrapper = new ModelWrapper{@code<>}();
- *
- * Property{@code} wrappedNameProperty = personWrapper.field(person -> person.getName(), (person, value) -> person.setName(value), "empty");
- *
- * // or with a method reference
- * Property{@code} wrappedNameProperty = personWrapper.field(Person::getName, Person::setName, "empty");
- *
- *
+ * 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 getter
@@ -508,140 +451,370 @@ public Property field(Function getter, BiConsumer 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.
* @param defaultValue
- * the default value for the field.
- * @param
- * the type of the field.
+ * the default value that is used when {@link #reset()} is invoked.
*
* @return The wrapped property instance.
*/
- public Property field(Function getter, BiConsumer setter, T defaultValue) {
- return add(new BeanPropertyField<>(getter, setter, defaultValue));
+ public StringProperty field(StringGetter getter, StringSetter setter, String defaultValue) {
+ return add(new BeanPropertyField<>(getter, setter, defaultValue, SimpleStringProperty::new));
}
-
/**
- * Add a new field to this instance of the wrapper that is identified by the given string. This method is basically
- * the same as {@link #field(Function)} with one difference: This method can be invoked multiply times but will only
- * create a single field instance for every given string identifier. This means that the returned property will be
- * the same instance for each call with the same identifier.
+ * 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.
*
- * This behaviour can be useful when you don't keep a reference to the returned property but directly call this
- * method in a property accessor method in your viewModel. This way only a single field wrapping will be defined
- * even when the accessor method is called multiple times. See the following example of a typical use case:
+ *
+ * Example:
*
- *
+ *
*
+ * ModelWrapper{@code} personWrapper = new ModelWrapper{@code<>}();
*
- * public class PersonViewModel extends ViewModel {
- *
- * // you only need a reference to the model wrapper itself but no additional references to each wrapped property.
- * private ModelWrapper{@code} wrapper = new ModelWrapper{@code<>}();
- *
- *
- * // This method will be used from the view.
- * // The view can call this method multiple times but will always get the same property instance.
- * public Property{@code} nameProperty() {
- * return wrapper.field("name", Person::nameProperty);
- * }
- * }
- *
- *
+ * StringProperty wrappedNameProperty = personWrapper.field(person -> person.nameProperty());
*
- * @param fieldName
- * the identifier for this field. Typically you will use the name of the field in the model.
+ * // or with a method reference
+ * StringProperty wrappedNameProperty = personWrapper.field(Person::nameProperty);
+ *
+ *
+ *
* @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.
- * @param
- * the type of the field.
+ *
* @return The wrapped property instance.
*/
- public Property field(String fieldName, Function> accessor) {
- return addIdentified(fieldName, new FxPropertyField<>(accessor));
+ public StringProperty field(StringPropertyAccessor accessor) {
+ return add(new FxPropertyField<>(accessor::apply, SimpleStringProperty::new));
}
/**
- * See {@link #field(String, Function)}. The difference is that this method accepts an additional parameter to
- * define the default value that will be used when {@link #reset()} is invoked.
- *
- * @param fieldName
- * the identifier for this field. Typically you will use the name of the field in the model.
+ * 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.
- * @param
- * the type of the field.
+ * @param defaultValue
+ * the default value that is used when {@link #reset()} is invoked.
* @return The wrapped property instance.
*/
- public Property field(String fieldName, Function> accessor, T defaultValue) {
- return addIdentified(fieldName, new FxPropertyField<>(accessor, defaultValue));
+ public StringProperty field(StringPropertyAccessor accessor, String defaultValue) {
+ return add(new FxPropertyField<>(accessor::apply, SimpleStringProperty::new));
}
+
+
+
/**
- * Add a new field to this instance of the wrapper that is identified by the given string. This method is basically
- * the same as {@link #field(Function, BiConsumer)} with one difference: This method can be invoked multiply times
- * but will only create a single field instance for every given string identifier. This means that the returned
- * property will be the same instance for each call with the same identifier.
- *
- * This behaviour can be useful when you don't keep a reference to the returned property but directly call this
- * method in a property accessor method in your viewModel. This way only a single field wrapping will be defined
- * even when the accessor method is called multiple times. See the following example of a typical use case:
- *
- *
- *
- *
- * public class PersonViewModel extends ViewModel {
- *
- * // you only need a reference to the model wrapper itself but no additional references to each wrapped property.
- * private ModelWrapper{@code} wrapper = new ModelWrapper{@code<>}();
- *
- *
- * // This method will be used from the view.
- * // The view can call this method multiple times but will always get the same property instance.
- * public Property{@code} nameProperty() {
- * return wrapper.field("name", Person::getName, Person::setName);
- * }
- * }
- *
+ * 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 fieldName
- * the identifier for this field. Typically you will use the name of the field in the model.
+ * @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 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.
- * @param
- * the type of the field.
* @return The wrapped property instance.
*/
- public Property field(String fieldName, Function getter, BiConsumer setter) {
- return addIdentified(fieldName, new BeanPropertyField<>(getter, setter));
+ public StringProperty field(String identifier, StringGetter getter, StringSetter setter) {
+ return addIdentified(identifier, new BeanPropertyField<>(getter, setter, SimpleStringProperty::new));
+ }
+
+ public StringProperty field(String identifier, StringGetter getter, StringSetter setter, String defaultValue) {
+ return addIdentified(identifier, new BeanPropertyField<>(getter, setter, defaultValue,
+ SimpleStringProperty::new));
}
/**
- * See {@link #field(String, Function, BiConsumer)}. The difference is that this method accepts an additional
- * parameter to define the default value that will be used when {@link #reset()} is invoked.
+ * 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.
*
- * @param fieldName
- * the identifier for this field. Typically you will use the name of the field in the model.
- * @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 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.
- * @param
- * the type of the field.
+ * 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 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 Property field(String fieldName, Function getter, BiConsumer setter, T defaultValue) {
- return addIdentified(fieldName, new BeanPropertyField<>(getter, setter, defaultValue));
+ public StringProperty field(String identifier, StringPropertyAccessor accessor) {
+ return addIdentified(identifier, new FxPropertyField<>(accessor::apply, SimpleStringProperty::new));
+ }
+
+ public StringProperty field(String identifier, StringPropertyAccessor accessor, String defaultValue) {
+ return addIdentified(identifier,
+ new FxPropertyField<>(accessor::apply, defaultValue, SimpleStringProperty::new));
+ }
+
+ /** Field type Boolean **/
+
+ public BooleanProperty field(BooleanGetter getter, BooleanSetter setter) {
+ return add(new BeanPropertyField<>(getter, setter, SimpleBooleanProperty::new));
+ }
+
+ public BooleanProperty field(BooleanGetter getter, BooleanSetter setter, boolean defaultValue) {
+ return add(new BeanPropertyField<>(getter, setter, defaultValue, SimpleBooleanProperty::new));
+ }
+
+ public BooleanProperty field(BooleanPropertyAccessor accessor) {
+ return add(new FxPropertyField<>(accessor, SimpleBooleanProperty::new));
+ }
+
+ public BooleanProperty field(BooleanPropertyAccessor accessor, boolean defaultValue) {
+ return add(new FxPropertyField<>(accessor, defaultValue, SimpleBooleanProperty::new));
+ }
+
+ public BooleanProperty field(String identifier, BooleanGetter getter, BooleanSetter setter) {
+ return addIdentified(identifier, new BeanPropertyField<>(getter, setter, SimpleBooleanProperty::new));
+ }
+
+ public BooleanProperty field(String identifier, BooleanGetter getter, BooleanSetter setter,
+ boolean defaultValue) {
+ return addIdentified(identifier, new BeanPropertyField<>(getter, setter, defaultValue,
+ SimpleBooleanProperty::new));
+ }
+
+ public BooleanProperty field(String identifier, BooleanPropertyAccessor accessor) {
+ return addIdentified(identifier, new FxPropertyField<>(accessor, SimpleBooleanProperty::new));
+ }
+
+ public BooleanProperty field(String identifier, BooleanPropertyAccessor accessor, boolean defaultValue) {
+ return addIdentified(identifier, new FxPropertyField<>(accessor, defaultValue, SimpleBooleanProperty::new));
+ }
+
+
+
+ /** Field type Double **/
+
+
+ public DoubleProperty field(DoubleGetter getter, DoubleSetter setter) {
+ return add(new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.doubleValue()),
+ SimpleDoubleProperty::new));
+ }
+
+ public DoubleProperty field(DoubleGetter getter, DoubleSetter setter, double defaultValue) {
+ return add(new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.doubleValue()),
+ defaultValue,
+ SimpleDoubleProperty::new));
+ }
+
+ public DoubleProperty field(DoublePropertyAccessor accessor) {
+ return add(new FxPropertyField<>(accessor::apply, SimpleDoubleProperty::new));
+ }
+
+ public DoubleProperty field(DoublePropertyAccessor accessor, double defaultValue) {
+ return add(new FxPropertyField<>(accessor::apply, defaultValue, SimpleDoubleProperty::new));
+ }
+
+ public DoubleProperty field(String identifier, DoubleGetter getter, DoubleSetter setter) {
+ return addIdentified(identifier,
+ new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.doubleValue()),
+ SimpleDoubleProperty::new));
+ }
+
+ public DoubleProperty field(String identifier, DoubleGetter getter, DoubleSetter setter, double defaultValue) {
+ return addIdentified(identifier,
+ new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.doubleValue()),
+ defaultValue,
+ SimpleDoubleProperty::new));
+ }
+
+ public DoubleProperty field(String identifier, DoublePropertyAccessor accessor) {
+ return addIdentified(identifier, new FxPropertyField<>(accessor::apply, SimpleDoubleProperty::new));
+ }
+
+ public DoubleProperty field(String identifier, DoublePropertyAccessor accessor, double defaultValue) {
+ return addIdentified(identifier,
+ new FxPropertyField<>(accessor::apply, defaultValue, SimpleDoubleProperty::new));
+ }
+
+
+
+
+ /** Field type Float **/
+
+ public FloatProperty field(FloatGetter getter, FloatSetter setter) {
+ return add(new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.floatValue()),
+ SimpleFloatProperty::new));
+ }
+
+ public FloatProperty field(FloatGetter getter, FloatSetter setter, float defaultValue) {
+ return add(new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.floatValue()),
+ defaultValue,
+ SimpleFloatProperty::new));
+ }
+
+ public FloatProperty field(FloatPropertyAccessor accessor) {
+ return add(new FxPropertyField<>(accessor::apply, SimpleFloatProperty::new));
+ }
+
+ public FloatProperty field(FloatPropertyAccessor accessor, float defaultValue) {
+ return add(new FxPropertyField<>(accessor::apply, defaultValue, SimpleFloatProperty::new));
+ }
+
+ public FloatProperty field(String identifier, FloatGetter getter, FloatSetter setter) {
+ return addIdentified(identifier,
+ new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.floatValue()),
+ SimpleFloatProperty::new));
+ }
+
+ public FloatProperty field(String identifier, FloatGetter getter, FloatSetter setter, float defaultValue) {
+ return addIdentified(identifier,
+ new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.floatValue()),
+ defaultValue,
+ SimpleFloatProperty::new));
+ }
+
+ public FloatProperty field(String identifier, FloatPropertyAccessor accessor) {
+ return addIdentified(identifier, new FxPropertyField<>(accessor::apply, SimpleFloatProperty::new));
+ }
+
+ public FloatProperty field(String identifier, FloatPropertyAccessor accessor, float defaultValue) {
+ return addIdentified(identifier, new FxPropertyField<>(accessor::apply, defaultValue, SimpleFloatProperty::new));
+ }
+
+
+ /** Field type Integer **/
+
+
+ public IntegerProperty field(IntGetter getter, IntSetter setter) {
+ return add(new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.intValue()),
+ SimpleIntegerProperty::new));
+ }
+
+ public IntegerProperty field(IntGetter getter, IntSetter setter, int defaultValue) {
+ return add(new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.intValue()),
+ defaultValue,
+ SimpleIntegerProperty::new));
+ }
+
+
+ public IntegerProperty field(IntPropertyAccessor accessor) {
+ return add(new FxPropertyField<>(accessor::apply, SimpleIntegerProperty::new));
+ }
+
+ public IntegerProperty field(IntPropertyAccessor accessor, int defaultValue) {
+ return add(new FxPropertyField<>(accessor::apply, defaultValue, SimpleIntegerProperty::new));
+ }
+
+ public IntegerProperty field(String identifier, IntGetter getter, IntSetter setter) {
+ return addIdentified(identifier,
+ new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.intValue()),
+ SimpleIntegerProperty::new));
+ }
+
+ public IntegerProperty field(String identifier, IntGetter getter, IntSetter setter, int defaultValue) {
+ return addIdentified(identifier,
+ new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.intValue()),
+ defaultValue,
+ SimpleIntegerProperty::new));
+ }
+
+
+ public IntegerProperty field(String identifier, IntPropertyAccessor accessor) {
+ return addIdentified(identifier, new FxPropertyField<>(accessor::apply, SimpleIntegerProperty::new));
+ }
+
+ public IntegerProperty field(String identifier, IntPropertyAccessor accessor, int defaultValue) {
+ return addIdentified(identifier, new FxPropertyField<>(accessor::apply, defaultValue,
+ SimpleIntegerProperty::new));
+ }
+
+
+
+ /** Field type Long **/
+
+ public LongProperty field(LongGetter getter, LongSetter setter) {
+ return add(new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.longValue()),
+ SimpleLongProperty::new));
+ }
+
+ public LongProperty field(LongGetter getter, LongSetter setter, long defaultValue) {
+ return add(new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.longValue()),
+ defaultValue,
+ SimpleLongProperty::new));
+ }
+
+ public LongProperty field(LongPropertyAccessor accessor) {
+ return add(new FxPropertyField<>(accessor::apply, SimpleLongProperty::new));
+ }
+
+ public LongProperty field(LongPropertyAccessor accessor, long defaultValue) {
+ return add(new FxPropertyField<>(accessor::apply, defaultValue, SimpleLongProperty::new));
+ }
+
+
+ public LongProperty field(String identifier, LongGetter getter, LongSetter setter) {
+ return addIdentified(identifier,
+ new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.longValue()),
+ SimpleLongProperty::new));
+ }
+
+ public LongProperty field(String identifier, LongGetter getter, LongSetter setter, long defaultValue) {
+ return addIdentified(identifier,
+ new BeanPropertyField<>(getter::apply, (m, number) -> setter.accept(m, number.longValue()),
+ defaultValue,
+ SimpleLongProperty::new));
+ }
+
+ public LongProperty field(String identifier, LongPropertyAccessor accessor) {
+ return addIdentified(identifier, new FxPropertyField<>(accessor::apply, SimpleLongProperty::new));
+ }
+
+ public LongProperty field(String identifier, LongPropertyAccessor accessor, long defaultValue) {
+ return addIdentified(identifier, new FxPropertyField<>(accessor::apply, defaultValue, SimpleLongProperty::new));
+ }
+
+
+
+ /** Field type generic **/
+
+
+ public ObjectProperty field(ObjectGetter getter, ObjectSetter setter) {
+ return add(new BeanPropertyField<>(getter, setter, SimpleObjectProperty::new));
+ }
+
+ public ObjectProperty field(ObjectGetter getter, ObjectSetter setter, T defaultValue) {
+ return add(new BeanPropertyField<>(getter, setter, defaultValue, SimpleObjectProperty::new));
+ }
+
+ public ObjectProperty field(ObjectPropertyAccessor accessor) {
+ return add(new FxPropertyField<>(accessor::apply, SimpleObjectProperty::new));
+ }
+
+ public ObjectProperty field(ObjectPropertyAccessor accessor, T defaultValue) {
+ return add(new FxPropertyField<>(accessor::apply, defaultValue, SimpleObjectProperty::new));
+ }
+
+
+ public ObjectProperty field(String identifier, ObjectGetter getter, ObjectSetter setter) {
+ return addIdentified(identifier, new BeanPropertyField<>(getter, setter, SimpleObjectProperty::new));
+ }
+
+ public ObjectProperty field(String identifier, ObjectGetter getter, ObjectSetter setter,
+ T defaultValue) {
+ return addIdentified(identifier, new BeanPropertyField<>(getter, setter, defaultValue,
+ SimpleObjectProperty::new));
+ }
+
+ public ObjectProperty field(String identifier, ObjectPropertyAccessor accessor) {
+ return addIdentified(identifier, new FxPropertyField<>(accessor::apply, SimpleObjectProperty::new));
+ }
+
+ public ObjectProperty field(String identifier, ObjectPropertyAccessor accessor, T defaultValue) {
+ return addIdentified(identifier,
+ new FxPropertyField<>(accessor::apply, defaultValue, SimpleObjectProperty::new));
}
- private Property add(PropertyField field) {
+ private > R add(PropertyField field) {
fields.add(field);
if (model != null) {
field.reload(model);
@@ -650,10 +823,10 @@ private Property add(PropertyField field) {
}
@SuppressWarnings("unchecked")
- private Property addIdentified(String fieldName, PropertyField field) {
+ private > R addIdentified(String fieldName, PropertyField field) {
if (identifiedFields.containsKey(fieldName)) {
final Property> property = identifiedFields.get(fieldName).getProperty();
- return (Property) property;
+ return (R) property;
} else {
identifiedFields.put(fieldName, field);
return add(field);
diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/BooleanGetter.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/BooleanGetter.java
new file mode 100644
index 000000000..639e3daee
--- /dev/null
+++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/BooleanGetter.java
@@ -0,0 +1,21 @@
+package de.saxsys.mvvmfx.utils.mapping.accessorfunctions;
+
+import java.util.function.Function;
+
+/**
+ * A functional interface to define a getter method of type {@link Boolean}.
+ *
+ * @param
+ * the generic type of the model.
+ */
+@FunctionalInterface
+public interface BooleanGetter extends Function {
+
+ /**
+ * @param model
+ * the model instance.
+ * @return the value of the field.
+ */
+ @Override
+ Boolean apply(M model);
+}
diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/BooleanPropertyAccessor.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/BooleanPropertyAccessor.java
new file mode 100644
index 000000000..f36e7aa18
--- /dev/null
+++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/BooleanPropertyAccessor.java
@@ -0,0 +1,22 @@
+package de.saxsys.mvvmfx.utils.mapping.accessorfunctions;
+
+import javafx.beans.property.Property;
+
+import java.util.function.Function;
+
+/**
+ * A functional interface to define an accessor method for a property of type {@link Boolean}.
+ *
+ * @param
+ * the generic type of the model.
+ */
+@FunctionalInterface
+public interface BooleanPropertyAccessor extends Function> {
+ /**
+ * @param model
+ * the model instance.
+ * @return the property field of the model.
+ */
+ @Override
+ Property apply(M model);
+}
diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/BooleanSetter.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/BooleanSetter.java
new file mode 100644
index 000000000..05ad65113
--- /dev/null
+++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/BooleanSetter.java
@@ -0,0 +1,22 @@
+package de.saxsys.mvvmfx.utils.mapping.accessorfunctions;
+
+import java.util.function.BiConsumer;
+
+/**
+ * A functional interface to define a setter method of type {@link Boolean}.
+ *
+ * @param