aboutsummaryrefslogtreecommitdiffstats
path: root/server/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/main')
-rw-r--r--server/src/main/java/com/vaadin/data/BeanBinder.java4
-rw-r--r--server/src/main/java/com/vaadin/data/Binder.java49
-rw-r--r--server/src/main/java/com/vaadin/data/HasValue.java28
-rw-r--r--server/src/main/java/com/vaadin/ui/AbstractColorPicker.java5
-rw-r--r--server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java9
-rw-r--r--server/src/main/java/com/vaadin/ui/AbstractTextField.java23
-rw-r--r--server/src/main/java/com/vaadin/ui/CheckBox.java5
-rw-r--r--server/src/main/java/com/vaadin/ui/RichTextArea.java15
-rw-r--r--server/src/main/java/com/vaadin/ui/Slider.java5
9 files changed, 105 insertions, 38 deletions
diff --git a/server/src/main/java/com/vaadin/data/BeanBinder.java b/server/src/main/java/com/vaadin/data/BeanBinder.java
index d8e8c0450d..96654e37a0 100644
--- a/server/src/main/java/com/vaadin/data/BeanBinder.java
+++ b/server/src/main/java/com/vaadin/data/BeanBinder.java
@@ -266,8 +266,8 @@ public class BeanBinder<BEAN> extends Binder<BEAN> {
@Override
public <FIELDVALUE> BeanBinding<BEAN, FIELDVALUE, FIELDVALUE> forField(
HasValue<FIELDVALUE> field) {
- return createBinding(field, Converter.identity(),
- this::handleValidationStatus);
+ return (BeanBinding<BEAN, FIELDVALUE, FIELDVALUE>) super.forField(
+ field);
}
/**
diff --git a/server/src/main/java/com/vaadin/data/Binder.java b/server/src/main/java/com/vaadin/data/Binder.java
index 1dfed366ba..8356a03dda 100644
--- a/server/src/main/java/com/vaadin/data/Binder.java
+++ b/server/src/main/java/com/vaadin/data/Binder.java
@@ -280,6 +280,23 @@ public class Binder<BEAN> implements Serializable {
}
/**
+ * Maps binding value {@code null} to given null representation and back
+ * to {@code null} when converting back to model value.
+ *
+ * @param nullRepresentation
+ * the value to use instead of {@code null}
+ * @return a new binding with null representation handling.
+ */
+ public default Binding<BEAN, FIELDVALUE, TARGET> withNullRepresentation(
+ TARGET nullRepresentation) {
+ return withConverter(
+ fieldValue -> Objects.equals(fieldValue, nullRepresentation)
+ ? null : fieldValue,
+ modelValue -> Objects.isNull(modelValue)
+ ? nullRepresentation : modelValue);
+ }
+
+ /**
* Gets the field the binding uses.
*
* @return the field for the binding
@@ -615,10 +632,6 @@ public class Binder<BEAN> implements Serializable {
return validationStatus;
}
- private void setBeanValue(BEAN bean, TARGET value) {
- setter.accept(bean, value);
- }
-
private void notifyStatusHandler(ValidationStatus<?> status) {
statusHandler.accept(status);
}
@@ -694,6 +707,13 @@ public class Binder<BEAN> implements Serializable {
* {@link Binding#bind(Function, BiConsumer) Binding.bind} which completes
* the binding. Until {@code Binding.bind} is called, the binding has no
* effect.
+ * <p>
+ * <strong>Note:</strong> Not all {@link HasValue} implementations support
+ * passing {@code null} as the value. For these the Binder will
+ * automatically change {@code null} to a null representation provided by
+ * {@link HasValue#getEmptyValue()}. This conversion is one-way only, if you
+ * want to have a two-way mapping back to {@code null}, use
+ * {@link Binding#withNullRepresentation(Object))}.
*
* @param <FIELDVALUE>
* the value type of the field
@@ -710,7 +730,10 @@ public class Binder<BEAN> implements Serializable {
clearError(field);
getStatusLabel().ifPresent(label -> label.setValue(""));
- return createBinding(field, Converter.identity(),
+ return createBinding(field, Converter.from(fieldValue -> fieldValue,
+ modelValue -> Objects.isNull(modelValue) ? field.getEmptyValue()
+ : modelValue,
+ exception -> exception.getMessage()),
this::handleValidationStatus);
}
@@ -722,6 +745,14 @@ public class Binder<BEAN> implements Serializable {
* Use the {@link #forField(HasValue)} overload instead if you want to
* further configure the new binding.
* <p>
+ * <strong>Note:</strong> Not all {@link HasValue} implementations support
+ * passing {@code null} as the value. For these the Binder will
+ * automatically change {@code null} to a null representation provided by
+ * {@link HasValue#getEmptyValue()}. This conversion is one-way only, if you
+ * want to have a two-way mapping back to {@code null}, use
+ * {@link #forField(HasValue)} and
+ * {@link Binding#withNullRepresentation(Object))}.
+ * <p>
* When a bean is bound with {@link Binder#bind(BEAN)}, the field value is
* set to the return value of the given getter. The property value is then
* updated via the given setter whenever the field value changes. The setter
@@ -905,8 +936,8 @@ public class Binder<BEAN> implements Serializable {
// Save old bean values so we can restore them if validators fail
Map<Binding<BEAN, ?, ?>, Object> oldValues = new HashMap<>();
- bindings.forEach(binding -> oldValues.put(binding,
- binding.convertDataToFieldType(bean)));
+ bindings.forEach(
+ binding -> oldValues.put(binding, binding.getter.apply(bean)));
bindings.forEach(binding -> binding.storeFieldValue(bean));
// Now run bean level validation against the updated bean
@@ -915,8 +946,8 @@ public class Binder<BEAN> implements Serializable {
.findAny().isPresent();
if (hasErrors) {
// Bean validator failed, revert values
- bindings.forEach((BindingImpl binding) -> binding.setBeanValue(bean,
- oldValues.get(binding)));
+ bindings.forEach((BindingImpl binding) -> binding.setter
+ .accept(bean, oldValues.get(binding)));
} else {
// Save successful, reset hasChanges to false
setHasChanges(false);
diff --git a/server/src/main/java/com/vaadin/data/HasValue.java b/server/src/main/java/com/vaadin/data/HasValue.java
index 89d8d69e66..0ad3203b78 100644
--- a/server/src/main/java/com/vaadin/data/HasValue.java
+++ b/server/src/main/java/com/vaadin/data/HasValue.java
@@ -17,6 +17,9 @@ package com.vaadin.data;
import java.io.Serializable;
import java.lang.reflect.Method;
+import java.util.Objects;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
import com.vaadin.event.ConnectorEvent;
import com.vaadin.event.EventListener;
@@ -170,4 +173,29 @@ public interface HasValue<V> extends Serializable {
*/
public Registration addValueChangeListener(
ValueChangeListener<? super V> listener);
+
+ /**
+ * Returns the value that represents an empty value.
+ * <p>
+ * By default {@link HasValue} is expected to support {@code null} as empty
+ * values. Specific implementations might not support this.
+ *
+ * @return empty value
+ * @see Binder#bind(HasValue, Function, BiConsumer)
+ */
+ public default V getEmptyValue() {
+ return null;
+ }
+
+ /**
+ * Returns whether this {@code HasValue} is considered to be empty.
+ * <p>
+ * By default this is an equality check between current value and empty
+ * value.
+ *
+ * @return {@code true} if considered empty; {@code false} if not
+ */
+ public default boolean isEmpty() {
+ return Objects.equals(getValue(), getEmptyValue());
+ }
}
diff --git a/server/src/main/java/com/vaadin/ui/AbstractColorPicker.java b/server/src/main/java/com/vaadin/ui/AbstractColorPicker.java
index dd482a5bd0..f256636845 100644
--- a/server/src/main/java/com/vaadin/ui/AbstractColorPicker.java
+++ b/server/src/main/java/com/vaadin/ui/AbstractColorPicker.java
@@ -534,4 +534,9 @@ public abstract class AbstractColorPicker extends AbstractField<Color> {
this.color = color;
getState().color = color.getCSS();
}
+
+ @Override
+ public Color getEmptyValue() {
+ return Color.WHITE;
+ }
}
diff --git a/server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java b/server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java
index 6c1fbde990..7332941f91 100644
--- a/server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java
+++ b/server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java
@@ -342,7 +342,7 @@ public abstract class AbstractMultiSelect<T>
* The call is delegated to {@link #getSelectedItems()}
*
* @return the current selection
- *
+ *
* @see #getSelectedItems()
* @see SelectionModel#getSelectedItems
*/
@@ -378,11 +378,16 @@ public abstract class AbstractMultiSelect<T>
new LinkedHashSet<>(getSelectionModel().getSelectedItems()));
}
+ @Override
+ public Set<T> getEmptyValue() {
+ return Collections.emptySet();
+ }
+
/**
* Adds a value change listener. The listener is called when the selection
* set of this multi select is changed either by the user or
* programmatically.
- *
+ *
* @see #addSelectionListener(MultiSelectionListener)
*
* @param listener
diff --git a/server/src/main/java/com/vaadin/ui/AbstractTextField.java b/server/src/main/java/com/vaadin/ui/AbstractTextField.java
index b5119ec92c..9c2720efd8 100644
--- a/server/src/main/java/com/vaadin/ui/AbstractTextField.java
+++ b/server/src/main/java/com/vaadin/ui/AbstractTextField.java
@@ -17,6 +17,7 @@
package com.vaadin.ui;
import java.util.Collection;
+import java.util.Objects;
import org.jsoup.nodes.Attributes;
import org.jsoup.nodes.Element;
@@ -80,11 +81,8 @@ public abstract class AbstractTextField extends AbstractField<String>
@Override
public void setValue(String value) {
- if (value == null) {
- setValue("", false);
- } else {
- setValue(value, false);
- }
+ Objects.requireNonNull(value, "Null value not supported");
+ setValue(value, false);
}
/**
@@ -270,16 +268,6 @@ public abstract class AbstractTextField extends AbstractField<String>
setValue("");
}
- /**
- * Checks if the field is empty.
- *
- * @return <code>true</code> if the field value is an empty string,
- * <code>false</code> otherwise
- */
- public boolean isEmpty() {
- return "".equals(getValue());
- }
-
@Override
public void writeDesign(Element design, DesignContext designContext) {
super.writeDesign(design, designContext);
@@ -299,4 +287,9 @@ public abstract class AbstractTextField extends AbstractField<String>
customAttributes.add("cursor-position");
return customAttributes;
}
+
+ @Override
+ public String getEmptyValue() {
+ return "";
+ }
}
diff --git a/server/src/main/java/com/vaadin/ui/CheckBox.java b/server/src/main/java/com/vaadin/ui/CheckBox.java
index 4fa08a82b5..c4bedbea0a 100644
--- a/server/src/main/java/com/vaadin/ui/CheckBox.java
+++ b/server/src/main/java/com/vaadin/ui/CheckBox.java
@@ -134,6 +134,11 @@ public class CheckBox extends AbstractField<Boolean>
}
@Override
+ public Boolean getEmptyValue() {
+ return false;
+ }
+
+ @Override
protected CheckBoxState getState() {
return (CheckBoxState) super.getState();
}
diff --git a/server/src/main/java/com/vaadin/ui/RichTextArea.java b/server/src/main/java/com/vaadin/ui/RichTextArea.java
index 49346928be..545166d33a 100644
--- a/server/src/main/java/com/vaadin/ui/RichTextArea.java
+++ b/server/src/main/java/com/vaadin/ui/RichTextArea.java
@@ -115,6 +115,11 @@ public class RichTextArea extends AbstractField<String>
}
@Override
+ public String getEmptyValue() {
+ return "";
+ }
+
+ @Override
protected void doSetValue(String value) {
getState().value = value;
}
@@ -152,16 +157,6 @@ public class RichTextArea extends AbstractField<String>
}
/**
- * Checks if the field is empty.
- *
- * @return <code>true</code> if the field value is an empty string,
- * <code>false</code> otherwise
- */
- public boolean isEmpty() {
- return getValue().length() == 0;
- }
-
- /**
* Clears the value of this field.
*/
public void clear() {
diff --git a/server/src/main/java/com/vaadin/ui/Slider.java b/server/src/main/java/com/vaadin/ui/Slider.java
index 7a59b86fca..99ab476e2a 100644
--- a/server/src/main/java/com/vaadin/ui/Slider.java
+++ b/server/src/main/java/com/vaadin/ui/Slider.java
@@ -294,6 +294,11 @@ public class Slider extends AbstractField<Double> {
return getState().value;
}
+ @Override
+ public Double getEmptyValue() {
+ return getMin();
+ }
+
/**
* Thrown when the value of the slider is about to be set to a value that is
* outside the valid range of the slider.