aboutsummaryrefslogtreecommitdiffstats
path: root/server/src
diff options
context:
space:
mode:
authorTeemu Suo-Anttila <teemusa@vaadin.com>2016-11-17 17:05:28 +0200
committerVaadin Code Review <review@vaadin.com>2016-11-25 13:13:00 +0000
commitd37b2d430eca6b0eeacb48626c0bbfb33d1502de (patch)
treea73527e2b0eef2e902572908c1c6a22d44a2ab27 /server/src
parentd63c1f9014e0a49e5250edd41bd5c7542901c267 (diff)
downloadvaadin-framework-d37b2d430eca6b0eeacb48626c0bbfb33d1502de.tar.gz
vaadin-framework-d37b2d430eca6b0eeacb48626c0bbfb33d1502de.zip
Reintroduce Grid Editor using Binder
This patch restores the bean type to BinderValidationStatusHandler Change-Id: I9ace77a492c4823c15591fb1426e9bd216895fb0
Diffstat (limited to 'server/src')
-rw-r--r--server/src/main/java/com/vaadin/data/Binder.java8
-rw-r--r--server/src/main/java/com/vaadin/data/BinderValidationStatusHandler.java11
-rw-r--r--server/src/main/java/com/vaadin/ui/Grid.java275
-rw-r--r--server/src/main/java/com/vaadin/ui/components/grid/EditorImpl.java310
-rw-r--r--server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java2
5 files changed, 595 insertions, 11 deletions
diff --git a/server/src/main/java/com/vaadin/data/Binder.java b/server/src/main/java/com/vaadin/data/Binder.java
index 771726ea3c..df6d72d79c 100644
--- a/server/src/main/java/com/vaadin/data/Binder.java
+++ b/server/src/main/java/com/vaadin/data/Binder.java
@@ -884,7 +884,7 @@ public class Binder<BEAN> implements Serializable {
private Label statusLabel;
- private BinderValidationStatusHandler statusHandler;
+ private BinderValidationStatusHandler<BEAN> statusHandler;
private boolean hasChanges = false;
@@ -1356,7 +1356,7 @@ public class Binder<BEAN> implements Serializable {
* @see Binding#withValidationStatusHandler(ValidationStatusHandler)
*/
public void setValidationStatusHandler(
- BinderValidationStatusHandler statusHandler) {
+ BinderValidationStatusHandler<BEAN> statusHandler) {
Objects.requireNonNull(statusHandler, "Cannot set a null "
+ BinderValidationStatusHandler.class.getSimpleName());
if (statusLabel != null) {
@@ -1377,7 +1377,7 @@ public class Binder<BEAN> implements Serializable {
* @return the status handler used, never <code>null</code>
* @see #setValidationStatusHandler(BinderStatusHandler)
*/
- public BinderValidationStatusHandler getValidationStatusHandler() {
+ public BinderValidationStatusHandler<BEAN> getValidationStatusHandler() {
return Optional.ofNullable(statusHandler)
.orElse(this::handleBinderValidationStatus);
}
@@ -1509,7 +1509,7 @@ public class Binder<BEAN> implements Serializable {
* validators
*/
protected void handleBinderValidationStatus(
- BinderValidationStatus<?> binderStatus) {
+ BinderValidationStatus<BEAN> binderStatus) {
// let field events go to binding status handlers
binderStatus.getFieldValidationStatuses()
.forEach(status -> ((BindingImpl<?, ?, ?>) status.getBinding())
diff --git a/server/src/main/java/com/vaadin/data/BinderValidationStatusHandler.java b/server/src/main/java/com/vaadin/data/BinderValidationStatusHandler.java
index 691b7bd6c0..1f3a95688b 100644
--- a/server/src/main/java/com/vaadin/data/BinderValidationStatusHandler.java
+++ b/server/src/main/java/com/vaadin/data/BinderValidationStatusHandler.java
@@ -23,8 +23,8 @@ import com.vaadin.ui.AbstractComponent;
/**
* Handler for {@link BinderValidationStatus} changes.
* <p>
- * {{@link Binder#setValidationStatusHandler(BinderStatusHandler) Register} an instance of
- * this class to be able to customize validation status handling.
+ * {{@link Binder#setValidationStatusHandler(BinderStatusHandler) Register} an
+ * instance of this class to be able to customize validation status handling.
* <p>
* The default handler will show
* {@link AbstractComponent#setComponentError(com.vaadin.server.ErrorMessage) an
@@ -39,9 +39,12 @@ import com.vaadin.ui.AbstractComponent;
* @see Binder#validate()
* @see ValidationStatus
*
+ * @param <BEAN>
+ * the bean type of binder
+ *
* @since 8.0
*/
-public interface BinderValidationStatusHandler
- extends Consumer<BinderValidationStatus<?>>, Serializable {
+public interface BinderValidationStatusHandler<BEAN>
+ extends Consumer<BinderValidationStatus<BEAN>>, Serializable {
}
diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java
index ee1547bfce..53e22a53d4 100644
--- a/server/src/main/java/com/vaadin/ui/Grid.java
+++ b/server/src/main/java/com/vaadin/ui/Grid.java
@@ -31,6 +31,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -39,6 +40,7 @@ import java.util.stream.Stream;
import org.jsoup.nodes.Element;
import com.vaadin.data.Binder;
+import com.vaadin.data.BinderValidationStatus;
import com.vaadin.data.SelectionModel;
import com.vaadin.event.ConnectorEvent;
import com.vaadin.event.ContextClickEvent;
@@ -48,6 +50,7 @@ import com.vaadin.server.Extension;
import com.vaadin.server.JsonCodec;
import com.vaadin.server.SerializableComparator;
import com.vaadin.server.SerializableFunction;
+import com.vaadin.server.data.DataCommunicator;
import com.vaadin.server.data.SortOrder;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.Registration;
@@ -63,6 +66,7 @@ import com.vaadin.shared.ui.grid.HeightMode;
import com.vaadin.shared.ui.grid.SectionState;
import com.vaadin.shared.util.SharedUtil;
import com.vaadin.ui.Grid.FooterRow;
+import com.vaadin.ui.components.grid.EditorImpl;
import com.vaadin.ui.components.grid.Footer;
import com.vaadin.ui.components.grid.Header;
import com.vaadin.ui.components.grid.Header.Row;
@@ -587,6 +591,11 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents {
.getSortOrder(order.getDirection()))
.forEach(s -> s.forEach(sortProperties::add));
getDataCommunicator().setBackEndSorting(sortProperties);
+
+ // Close grid editor if it's open.
+ if (getEditor().isOpen()) {
+ getEditor().cancel();
+ }
}
@Override
@@ -795,6 +804,8 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents {
private StyleGenerator<T> styleGenerator = item -> null;
private DescriptionGenerator<T> descriptionGenerator;
+ private SerializableFunction<T, Component> componentGenerator;
+
/**
* Constructs a new Column configuration with given header caption,
* renderer and value provider.
@@ -1498,8 +1509,85 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents {
}
/**
+ * Sets whether this Column has a component displayed in Editor or not.
+ *
+ * @param editable
+ * {@code true} if column is editable; {@code false} if not
+ * @return this column
+ *
+ * @see #setEditorComponent(Component)
+ * @see #setEditorComponentGenerator(SerializableFunction)
+ */
+ public Column<T, V> setEditable(boolean editable) {
+ Objects.requireNonNull(componentGenerator,
+ "Column has no editor component defined");
+ getState().editable = editable;
+ return this;
+ }
+
+ /**
+ * Gets whether this Column has a component displayed in Editor or not.
+ *
+ * @return {@code true} if the column displays an editor component;
+ * {@code false} if not
+ */
+ public boolean isEditable() {
+ return getState(false).editable;
+ }
+
+ /**
+ * Sets a static editor component for this column.
+ * <p>
+ * <strong>Note:</strong> The same component cannot be used for multiple
+ * columns.
+ *
+ * @param component
+ * the editor component
+ * @return this column
+ *
+ * @see Editor#getBinder()
+ * @see Editor#setBinder(Binder)
+ * @see #setEditorComponentGenerator(SerializableFunction)
+ */
+ public Column<T, V> setEditorComponent(Component component) {
+ Objects.requireNonNull(component,
+ "null is not a valid editor field");
+ return setEditorComponentGenerator(t -> component);
+ }
+
+ /**
+ * Sets a component generator to provide editor component for this
+ * Column. This method can be used to generate any dynamic component to
+ * be displayed in the editor row.
+ * <p>
+ * <strong>Note:</strong> The same component cannot be used for multiple
+ * columns.
+ *
+ * @param componentGenerator
+ * the editor component generator
+ * @return this column
+ *
+ * @see #setEditorComponent(Component)
+ */
+ public Column<T, V> setEditorComponentGenerator(
+ SerializableFunction<T, Component> componentGenerator) {
+ Objects.requireNonNull(componentGenerator);
+ this.componentGenerator = componentGenerator;
+ return setEditable(true);
+ }
+
+ /**
+ * Gets the editor component generator for this Column.
+ *
+ * @return editor component generator
+ */
+ public SerializableFunction<T, Component> getEditorComponentGenerator() {
+ return componentGenerator;
+ }
+
+ /**
* Checks if column is attached and throws an
- * {@link IllegalStateException} if it is not
+ * {@link IllegalStateException} if it is not.
*
* @throws IllegalStateException
* if the column is no longer attached to any grid
@@ -1557,7 +1645,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents {
* The cells to merge. Must be from the same row.
* @return The remaining visible cell after the merge
*/
- HeaderCell join(HeaderCell ... cellsToMerge);
+ HeaderCell join(HeaderCell... cellsToMerge);
}
@@ -1716,6 +1804,167 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents {
public GridStaticCellType getCellType();
}
+ /**
+ * Generator for creating editor validation and conversion error messages.
+ *
+ * @param <T>
+ * the bean type
+ */
+ public interface EditorErrorGenerator<T> extends Serializable,
+ BiFunction<Map<Component, Column<T, ?>>, BinderValidationStatus<T>, String> {
+
+ /**
+ * Generates an error message from given validation status object.
+ *
+ * @param fieldToColumn
+ * the map of failed fields and corresponding columns
+ * @param status
+ * the binder status object with all failures
+ *
+ * @return error message string
+ */
+ @Override
+ public String apply(Map<Component, Column<T, ?>> fieldToColumn,
+ BinderValidationStatus<T> status);
+ }
+
+ /**
+ * An editor in a Grid.
+ *
+ * @param <T>
+ */
+ public interface Editor<T> extends Serializable {
+
+ /**
+ * Sets the underlying Binder to this Editor.
+ *
+ * @param binder
+ * the binder for updating editor fields; not {@code null}
+ * @return this editor
+ */
+ public Editor<T> setBinder(Binder<T> binder);
+
+ /**
+ * Returns the underlying Binder from Editor.
+ *
+ * @return the binder; not {@code null}
+ */
+ public Binder<T> getBinder();
+
+ /**
+ * Sets the Editor buffered mode. When the editor is in buffered mode,
+ * edits are only committed when the user clicks the save button. In
+ * unbuffered mode valid changes are automatically committed.
+ *
+ * @param buffered
+ * {@code true} if editor should be buffered; {@code false}
+ * if not
+ * @return this editor
+ */
+ public Editor<T> setBuffered(boolean buffered);
+
+ /**
+ * Enables or disabled the Editor. A disabled editor cannot be opened.
+ *
+ * @param enabled
+ * {@code true} if editor should be enabled; {@code false} if
+ * not
+ * @return this editor
+ */
+ public Editor<T> setEnabled(boolean enabled);
+
+ /**
+ * Returns whether Editor is buffered or not.
+ *
+ * @see #setBuffered(boolean)
+ *
+ * @return {@code true} if editor is buffered; {@code false} if not
+ */
+ public boolean isBuffered();
+
+ /**
+ * Returns whether Editor is enabled or not.
+ *
+ * @return {@code true} if editor is enabled; {@code false} if not
+ */
+ public boolean isEnabled();
+
+ /**
+ * Returns whether Editor is open or not.
+ *
+ * @return {@code true} if editor is open; {@code false} if not
+ */
+ public boolean isOpen();
+
+ /**
+ * Saves any changes from the Editor fields to the edited bean.
+ *
+ * @return {@code true} if save succeeded; {@code false} if not
+ */
+ public boolean save();
+
+ /**
+ * Close the editor discarding any unsaved changes.
+ */
+ public void cancel();
+
+ /**
+ * Sets the caption of the save button in buffered mode.
+ *
+ * @param saveCaption
+ * the save button caption
+ * @return this editor
+ */
+ public Editor<T> setSaveCaption(String saveCaption);
+
+ /**
+ * Sets the caption of the cancel button in buffered mode.
+ *
+ * @param cancelCaption
+ * the cancel button caption
+ * @return this editor
+ */
+ public Editor<T> setCancelCaption(String cancelCaption);
+
+ /**
+ * Gets the caption of the save button in buffered mode.
+ *
+ * @return the save button caption
+ */
+ public String getSaveCaption();
+
+ /**
+ * Gets the caption of the cancel button in buffered mode.
+ *
+ * @return the cancel button caption
+ */
+ public String getCancelCaption();
+
+ /**
+ * Sets the error message generator for this editor.
+ * <p>
+ * The default message is a concatenation of column field validation
+ * failures and bean validation failures.
+ *
+ * @param errorGenerator
+ * the function to generate error messages; not {@code null}
+ * @return this editor
+ *
+ * @see EditorErrorGenerator
+ */
+ public Editor<T> setErrorGenerator(
+ EditorErrorGenerator<T> errorGenerator);
+
+ /**
+ * Gets the error message generator of this editor.
+ *
+ * @return the function that generates error messages; not {@code null}
+ *
+ * @see EditorErrorGenerator
+ */
+ public EditorErrorGenerator<T> getErrorGenerator();
+ }
+
private class HeaderImpl extends Header {
@Override
@@ -1768,6 +2017,8 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents {
private GridSelectionModel<T> selectionModel;
+ private Editor<T> editor;
+
/**
* Constructor for the {@link Grid} component.
*/
@@ -1782,6 +2033,11 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents {
addExtension(detailsManager);
addDataGenerator(detailsManager);
+ editor = createEditor();
+ if (editor instanceof Extension) {
+ addExtension((Extension) editor);
+ }
+
addDataGenerator((item, json) -> {
String styleName = styleGenerator.apply(item);
if (styleName != null && !styleName.isEmpty()) {
@@ -2651,6 +2907,10 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents {
return ((SingleSelectionModel<T>) model).asSingleSelect();
}
+ public Editor<T> getEditor() {
+ return editor;
+ }
+
/**
* Sets the selection model for this listing.
* <p>
@@ -2675,6 +2935,17 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents {
return (GridState) super.getState(markAsDirty);
}
+ /**
+ * Creates a new Editor instance. Can be overridden to create a custom
+ * Editor. If the Editor is a {@link AbstractGridExtension}, it will be
+ * automatically added to {@link DataCommunicator}.
+ *
+ * @return editor
+ */
+ protected Editor<T> createEditor() {
+ return new EditorImpl<>();
+ }
+
private void addExtensionComponent(Component c) {
if (extensionComponents.add(c)) {
c.setParent(this);
diff --git a/server/src/main/java/com/vaadin/ui/components/grid/EditorImpl.java b/server/src/main/java/com/vaadin/ui/components/grid/EditorImpl.java
new file mode 100644
index 0000000000..bc83547488
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/components/grid/EditorImpl.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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 com.vaadin.ui.components.grid;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import com.vaadin.data.Binder;
+import com.vaadin.data.BinderValidationStatus;
+import com.vaadin.data.BinderValidationStatusHandler;
+import com.vaadin.shared.ui.grid.editor.EditorClientRpc;
+import com.vaadin.shared.ui.grid.editor.EditorServerRpc;
+import com.vaadin.shared.ui.grid.editor.EditorState;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.Grid.AbstractGridExtension;
+import com.vaadin.ui.Grid.Column;
+import com.vaadin.ui.Grid.Editor;
+import com.vaadin.ui.Grid.EditorErrorGenerator;
+
+import elemental.json.JsonObject;
+
+/**
+ * Implementation of {@code Editor} interface.
+ *
+ * @param <T>
+ * the grid bean type
+ */
+public class EditorImpl<T> extends AbstractGridExtension<T>
+ implements Editor<T> {
+
+ private class EditorStatusHandler
+ implements BinderValidationStatusHandler<T> {
+
+ @Override
+ public void accept(BinderValidationStatus<T> status) {
+ boolean ok = status.isOk();
+ if (saving) {
+ rpc.confirmSave(ok);
+ saving = false;
+ }
+
+ if (ok) {
+ binder.getBean().ifPresent(t -> refresh(t));
+ rpc.setErrorMessage(null, Collections.emptyList());
+ } else {
+ List<Component> fields = status.getFieldValidationErrors()
+ .stream().map(error -> error.getField())
+ .filter(columnFields.values()::contains)
+ .map(field -> (Component) field)
+ .collect(Collectors.toList());
+
+ Map<Component, Column<T, ?>> fieldToColumn = new HashMap<>();
+ columnFields.entrySet().stream()
+ .filter(entry -> fields.contains(entry.getValue()))
+ .forEach(entry -> fieldToColumn.put(entry.getValue(),
+ entry.getKey()));
+
+ String message = errorGenerator.apply(fieldToColumn, status);
+
+ List<String> columnIds = fieldToColumn.values().stream()
+ .map(Column::getId).collect(Collectors.toList());
+
+ rpc.setErrorMessage(message, columnIds);
+ }
+ }
+
+ }
+
+ private Binder<T> binder;
+ private Map<Column<T, ?>, Component> columnFields = new HashMap<>();
+ private T edited;
+ private boolean saving = false;
+ private EditorClientRpc rpc;
+ private EditorErrorGenerator<T> errorGenerator = (fieldToColumn,
+ status) -> {
+ String message = status.getFieldValidationErrors().stream()
+ .filter(e -> e.getMessage().isPresent()
+ && fieldToColumn.containsKey(e.getField()))
+ .map(e -> fieldToColumn.get(e.getField()).getCaption() + ": "
+ + e.getMessage().get())
+ .collect(Collectors.joining("; "));
+
+ String beanMessage = status.getBeanValidationErrors().stream()
+ .map(e -> e.getErrorMessage())
+ .collect(Collectors.joining("; "));
+
+ message = Stream.of(message, beanMessage).filter(s -> !s.isEmpty())
+ .collect(Collectors.joining("; "));
+
+ return message;
+ };
+
+ /**
+ * Constructor for internal implementation of the Editor.
+ */
+ public EditorImpl() {
+ rpc = getRpcProxy(EditorClientRpc.class);
+ registerRpc(new EditorServerRpc() {
+
+ @Override
+ public void save() {
+ saving = true;
+ EditorImpl.this.save();
+ }
+
+ @Override
+ public void cancel() {
+ doClose();
+ }
+
+ @Override
+ public void bind(String key) {
+ // When in buffered mode, the editor is not allowed to move.
+ // Binder with failed validation returns true for hasChanges.
+ if (isOpen() && (isBuffered() || getBinder().hasChanges())) {
+ rpc.confirmBind(false);
+ return;
+ }
+ doClose();
+ doEdit(getData(key));
+ rpc.confirmBind(true);
+ }
+ });
+
+ setBinder(new Binder<>());
+ }
+
+ @Override
+ public void generateData(T item, JsonObject jsonObject) {
+ }
+
+ @Override
+ public Editor<T> setBinder(Binder<T> binder) {
+ this.binder = binder;
+
+ binder.setValidationStatusHandler(new EditorStatusHandler());
+ return this;
+ }
+
+ @Override
+ public Binder<T> getBinder() {
+ return binder;
+ }
+
+ @Override
+ public Editor<T> setBuffered(boolean buffered) {
+ if (isOpen()) {
+ throw new IllegalStateException(
+ "Cannot modify Editor when it is open.");
+ }
+ getState().buffered = buffered;
+
+ return this;
+ }
+
+ @Override
+ public Editor<T> setEnabled(boolean enabled) {
+ if (isOpen()) {
+ throw new IllegalStateException(
+ "Cannot modify Editor when it is open.");
+ }
+ getState().enabled = enabled;
+
+ return this;
+ }
+
+ @Override
+ public boolean isBuffered() {
+ return getState(false).buffered;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return getState(false).enabled;
+ }
+
+ /**
+ * Handles editor component generation and adding them to the hierarchy of
+ * the Grid.
+ *
+ * @param bean
+ * the edited item; can't be {@code null}
+ */
+ protected void doEdit(T bean) {
+ Objects.requireNonNull(bean, "Editor can't edit null");
+ if (!isEnabled()) {
+ throw new IllegalStateException(
+ "Editing is not allowed when Editor is disabled.");
+ }
+
+ if (!isBuffered()) {
+ binder.setBean(bean);
+ } else {
+ binder.readBean(bean);
+ }
+ edited = bean;
+
+ getParent().getColumns().stream().filter(Column::isEditable)
+ .forEach(c -> {
+ Component component = c.getEditorComponentGenerator()
+ .apply(edited);
+ addComponentToGrid(component);
+ columnFields.put(c, component);
+ getState().columnFields.put(c.getId(),
+ component.getConnectorId());
+ });
+ }
+
+ @Override
+ public boolean save() {
+ if (isOpen() && isBuffered()) {
+ binder.validate();
+ if (binder.writeBeanIfValid(edited)) {
+ refresh(edited);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return edited != null;
+ }
+
+ @Override
+ public void cancel() {
+ doClose();
+ rpc.cancel();
+ }
+
+ /**
+ * Handles clean up for closing the Editor.
+ */
+ protected void doClose() {
+ edited = null;
+
+ for (Component c : columnFields.values()) {
+ removeComponentFromGrid(c);
+ }
+ columnFields.clear();
+ getState().columnFields.clear();
+ }
+
+ @Override
+ public Editor<T> setSaveCaption(String saveCaption) {
+ Objects.requireNonNull(saveCaption);
+ getState().saveCaption = saveCaption;
+
+ return this;
+ }
+
+ @Override
+ public Editor<T> setCancelCaption(String cancelCaption) {
+ Objects.requireNonNull(cancelCaption);
+ getState().cancelCaption = cancelCaption;
+
+ return this;
+ }
+
+ @Override
+ public String getSaveCaption() {
+ return getState(false).saveCaption;
+ }
+
+ @Override
+ public String getCancelCaption() {
+ return getState(false).cancelCaption;
+ }
+
+ @Override
+ protected EditorState getState() {
+ return getState(true);
+ }
+
+ @Override
+ protected EditorState getState(boolean markAsDirty) {
+ return (EditorState) super.getState(markAsDirty);
+ }
+
+ @Override
+ public Editor<T> setErrorGenerator(EditorErrorGenerator<T> errorGenerator) {
+ Objects.requireNonNull(errorGenerator, "Error generator can't be null");
+ this.errorGenerator = errorGenerator;
+ return this;
+ }
+
+ @Override
+ public EditorErrorGenerator<T> getErrorGenerator() {
+ return errorGenerator;
+ }
+} \ No newline at end of file
diff --git a/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java b/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java
index a621549b83..14a6be0307 100644
--- a/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java
+++ b/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java
@@ -637,7 +637,7 @@ public class BinderBookOfVaadinTest {
public void withBinderStatusHandlerExample() {
Label formStatusLabel = new Label();
- BinderValidationStatusHandler defaultHandler = binder
+ BinderValidationStatusHandler<BookPerson> defaultHandler = binder
.getValidationStatusHandler();
binder.setValidationStatusHandler(status -> {