summaryrefslogtreecommitdiffstats
path: root/server/src/main/java/com/vaadin/data
diff options
context:
space:
mode:
authorDenis Anisimov <denis@vaadin.com>2016-10-03 15:42:10 +0300
committerAleksi Hietanen <aleksi@vaadin.com>2016-10-06 08:44:50 +0000
commit3a69a723e689b047325ed88392d012ac96f4f62d (patch)
tree0e0034dcc7bd78e403aaad517fe242021ef1ca69 /server/src/main/java/com/vaadin/data
parent6abb9c5c64c60db47e8f2dde87afaf0e76b2228c (diff)
downloadvaadin-framework-3a69a723e689b047325ed88392d012ac96f4f62d.tar.gz
vaadin-framework-3a69a723e689b047325ed88392d012ac96f4f62d.zip
Add support for binder status change events (#208).
Change-Id: Ic8dee407569ee310f007ebe32660a1d2922e9493
Diffstat (limited to 'server/src/main/java/com/vaadin/data')
-rw-r--r--server/src/main/java/com/vaadin/data/Binder.java123
-rw-r--r--server/src/main/java/com/vaadin/data/StatusChangeEvent.java86
-rw-r--r--server/src/main/java/com/vaadin/data/StatusChangeListener.java37
3 files changed, 222 insertions, 24 deletions
diff --git a/server/src/main/java/com/vaadin/data/Binder.java b/server/src/main/java/com/vaadin/data/Binder.java
index d2114e8120..7a59b90946 100644
--- a/server/src/main/java/com/vaadin/data/Binder.java
+++ b/server/src/main/java/com/vaadin/data/Binder.java
@@ -34,6 +34,7 @@ import java.util.stream.Collectors;
import com.vaadin.data.util.converter.Converter;
import com.vaadin.data.util.converter.StringToIntegerConverter;
+import com.vaadin.event.EventRouter;
import com.vaadin.server.ErrorMessage;
import com.vaadin.server.UserError;
import com.vaadin.shared.Registration;
@@ -435,6 +436,7 @@ public class Binder<BEAN> implements Serializable {
this.setter = setter;
getBinder().bindings.add(this);
getBinder().getBean().ifPresent(this::bind);
+ getBinder().fireStatusChangeEvent(false);
}
@Override
@@ -533,6 +535,7 @@ public class Binder<BEAN> implements Serializable {
getBinder().getValidationStatusHandler()
.accept(new BinderValidationStatus<>(getBinder(),
Arrays.asList(status), Collections.emptyList()));
+ getBinder().fireStatusChangeEvent(status.isError());
return status;
}
@@ -544,8 +547,8 @@ public class Binder<BEAN> implements Serializable {
*/
private ValidationStatus<TARGET> doValidation() {
FIELDVALUE fieldValue = field.getValue();
- Result<TARGET> dataValue = converterValidatorChain.convertToModel(
- fieldValue, findLocale());
+ Result<TARGET> dataValue = converterValidatorChain
+ .convertToModel(fieldValue, findLocale());
return new ValidationStatus<>(this, dataValue);
}
@@ -566,8 +569,8 @@ public class Binder<BEAN> implements Serializable {
}
private FIELDVALUE convertDataToFieldType(BEAN bean) {
- return converterValidatorChain.convertToPresentation(
- getter.apply(bean), findLocale());
+ return converterValidatorChain
+ .convertToPresentation(getter.apply(bean), findLocale());
}
/**
@@ -577,7 +580,7 @@ public class Binder<BEAN> implements Serializable {
* the new value
*/
private void handleFieldValueChange(BEAN bean) {
- binder.setHasChanges(true);
+ getBinder().setHasChanges(true);
// store field value if valid
ValidationStatus<TARGET> fieldValidationStatus = storeFieldValue(
bean);
@@ -585,14 +588,15 @@ public class Binder<BEAN> implements Serializable {
// if all field level validations pass, run bean level validation
if (!getBinder().bindings.stream().map(BindingImpl::doValidation)
.anyMatch(ValidationStatus::isError)) {
- binderValidationResults = binder.validateItem(bean);
+ binderValidationResults = getBinder().validateItem(bean);
} else {
binderValidationResults = Collections.emptyList();
}
- binder.getValidationStatusHandler()
- .accept(new BinderValidationStatus<>(binder,
- Arrays.asList(fieldValidationStatus),
- binderValidationResults));
+ BinderValidationStatus<BEAN> status = new BinderValidationStatus<>(
+ binder, Arrays.asList(fieldValidationStatus),
+ binderValidationResults);
+ getBinder().getValidationStatusHandler().accept(status);
+ getBinder().fireStatusChangeEvent(status.hasErrors());
}
/**
@@ -668,6 +672,8 @@ public class Binder<BEAN> implements Serializable {
private final List<Validator<? super BEAN>> validators = new ArrayList<>();
+ private EventRouter eventRouter;
+
private Label statusLabel;
private BinderValidationStatusHandler statusHandler;
@@ -742,9 +748,9 @@ public class Binder<BEAN> implements Serializable {
@Override
public Registration addValueChangeListener(
ValueChangeListener<? super SELECTVALUE> listener) {
- return select.addSelectionListener(e -> listener.accept(
- new ValueChange<>(select, getValue(), e
- .isUserOriginated())));
+ return select.addSelectionListener(
+ e -> listener.accept(new ValueChange<>(select,
+ getValue(), e.isUserOriginated())));
}
});
}
@@ -916,7 +922,7 @@ public class Binder<BEAN> implements Serializable {
* <pre>
* class Feature {
* public enum Browser { CHROME, EDGE, FIREFOX, IE, OPERA, SAFARI }
-
+
* public Set&lt;Browser> getSupportedBrowsers() { ... }
* public void setSupportedBrowsers(Set&lt;Browser> title) { ... }
* }
@@ -964,13 +970,14 @@ public class Binder<BEAN> implements Serializable {
*/
public void bind(BEAN bean) {
Objects.requireNonNull(bean, "bean cannot be null");
- unbind();
+ doUnbind(false);
this.bean = bean;
bindings.forEach(b -> b.bind(bean));
// if there has been field value change listeners that trigger
// validation, need to make sure the validation errors are cleared
getValidationStatusHandler()
.accept(BinderValidationStatus.createUnresolvedStatus(this));
+ fireStatusChangeEvent(false);
}
/**
@@ -978,13 +985,7 @@ public class Binder<BEAN> implements Serializable {
* nothing.
*/
public void unbind() {
- setHasChanges(false);
- if (bean != null) {
- bean = null;
- bindings.forEach(BindingImpl::unbind);
- }
- getValidationStatusHandler()
- .accept(BinderValidationStatus.createUnresolvedStatus(this));
+ doUnbind(true);
}
/**
@@ -1009,6 +1010,7 @@ public class Binder<BEAN> implements Serializable {
getValidationStatusHandler()
.accept(BinderValidationStatus.createUnresolvedStatus(this));
+ fireStatusChangeEvent(false);
}
/**
@@ -1073,6 +1075,7 @@ public class Binder<BEAN> implements Serializable {
* @return a list of field validation errors if such occur, otherwise a list
* of bean validation errors.
*/
+ @SuppressWarnings({ "rawtypes", "unchecked" })
private BinderValidationStatus<BEAN> doSaveIfValid(BEAN bean) {
Objects.requireNonNull(bean, "bean cannot be null");
// First run fields level validation
@@ -1080,6 +1083,7 @@ public class Binder<BEAN> implements Serializable {
// If no validation errors then update bean
if (bindingStatuses.stream().filter(ValidationStatus::isError).findAny()
.isPresent()) {
+ fireStatusChangeEvent(true);
return new BinderValidationStatus<>(this, bindingStatuses,
Collections.emptyList());
}
@@ -1092,8 +1096,9 @@ public class Binder<BEAN> implements Serializable {
bindings.forEach(binding -> binding.storeFieldValue(bean));
// Now run bean level validation against the updated bean
List<Result<?>> binderResults = validateItem(bean);
- if (binderResults.stream().filter(Result::isError).findAny()
- .isPresent()) {
+ boolean hasErrors = binderResults.stream().filter(Result::isError)
+ .findAny().isPresent();
+ if (hasErrors) {
// Item validator failed, revert values
bindings.forEach((BindingImpl binding) -> binding.setBeanValue(bean,
oldValues.get(binding)));
@@ -1101,6 +1106,7 @@ public class Binder<BEAN> implements Serializable {
// Save successful, reset hasChanges to false
setHasChanges(false);
}
+ fireStatusChangeEvent(hasErrors);
return new BinderValidationStatus<>(this, bindingStatuses,
binderResults);
}
@@ -1150,6 +1156,7 @@ public class Binder<BEAN> implements Serializable {
bindingStatuses, validateItem(bean));
}
getValidationStatusHandler().accept(validationStatus);
+ fireStatusChangeEvent(validationStatus.hasErrors());
return validationStatus;
}
@@ -1274,6 +1281,43 @@ public class Binder<BEAN> implements Serializable {
}
/**
+ * Adds status change listener to the binder.
+ * <p>
+ * The {@link Binder} status is changed whenever any of the following
+ * happens:
+ * <ul>
+ * <li>if it's bound and any of its bound field or select has been changed
+ * <li>{@link #save(Object)} or {@link #saveIfValid(Object)} is called
+ * <li>{@link #load(Object)} is called
+ * <li>{@link #bind(Object)} is called
+ * <li>{@link #unbind(Object)} is called
+ * <li>{@link Binding#bind(Function, BiConsumer)} is called
+ * <li>{@link Binder#validate()} or {@link Binding#validate()} is called
+ * </ul>
+ *
+ * @see #load(Object)
+ * @see #save(Object)
+ * @see #saveIfValid(Object)
+ * @see #bind(Object)
+ * @see #unbind()
+ * @see #forField(HasValue)
+ * @see #forSelect(AbstractMultiSelect)
+ * @See {@link #validate()}
+ * @see Binding#validate()
+ * @see Binding#bind(Object)
+ *
+ * @param listener
+ * status change listener to add, not null
+ * @return a registration for the listener
+ */
+ public Registration addStatusChangeListener(StatusChangeListener listener) {
+ getEventRouter().addListener(StatusChangeEvent.class, listener,
+ StatusChangeListener.class.getDeclaredMethods()[0]);
+ return () -> getEventRouter().removeListener(StatusChangeEvent.class,
+ listener);
+ }
+
+ /**
* Creates a new binding with the given field.
*
* @param <FIELDVALUE>
@@ -1400,4 +1444,35 @@ public class Binder<BEAN> implements Serializable {
public boolean hasChanges() {
return hasChanges;
}
+
+ /**
+ * Returns the event router for this binder.
+ *
+ * @return the event router, not null
+ */
+ protected EventRouter getEventRouter() {
+ if (eventRouter == null) {
+ eventRouter = new EventRouter();
+ }
+ return eventRouter;
+ }
+
+ private void doUnbind(boolean fireStatusEvent) {
+ setHasChanges(false);
+ if (bean != null) {
+ bean = null;
+ bindings.forEach(BindingImpl::unbind);
+ }
+ getValidationStatusHandler()
+ .accept(BinderValidationStatus.createUnresolvedStatus(this));
+ if (fireStatusEvent) {
+ fireStatusChangeEvent(false);
+ }
+ }
+
+ private void fireStatusChangeEvent(boolean hasValidationErrors) {
+ getEventRouter()
+ .fireEvent(new StatusChangeEvent(this, hasValidationErrors));
+ }
+
}
diff --git a/server/src/main/java/com/vaadin/data/StatusChangeEvent.java b/server/src/main/java/com/vaadin/data/StatusChangeEvent.java
new file mode 100644
index 0000000000..7a73fb5150
--- /dev/null
+++ b/server/src/main/java/com/vaadin/data/StatusChangeEvent.java
@@ -0,0 +1,86 @@
+/*
+ * 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.data;
+
+import java.util.EventObject;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+import com.vaadin.data.Binder.Binding;
+
+/**
+ * Binder status change event.
+ * <p>
+ * The {@link Binder} status is changed whenever any of the following happens:
+ * <ul>
+ * <li>if it's bound and any of its bound field or select has been changed
+ * <li>{@link #save(Object)} or {@link #saveIfValid(Object)} is called
+ * <li>{@link #load(Object)} is called
+ * <li>{@link #bind(Object)} is called
+ * <li>{@link #unbind(Object)} is called
+ * <li>{@link Binding#bind(Function, BiConsumer)} is called
+ * <li>{@link Binder#validate()} or {@link Binding#validate()} is called
+ * </ul>
+ *
+ * @see StatusChangeListener#statusChange(StatusChangeEvent)
+ * @see Binder#addStatusChangeListener(StatusChangeListener)
+ *
+ * @author Vaadin Ltd
+ *
+ */
+public class StatusChangeEvent extends EventObject {
+
+ private final boolean hasValidationErrors;
+
+ /**
+ * Create a new status change event for given {@code binder} using its
+ * current validation status.
+ *
+ * @param binder
+ * the event source binder
+ * @param hasValidationErrors
+ * the binder validation status
+ */
+ public StatusChangeEvent(Binder<?> binder, boolean hasValidationErrors) {
+ super(binder);
+ this.hasValidationErrors = hasValidationErrors;
+ }
+
+ /**
+ * Gets the binder validation status.
+ *
+ * @return {@code true} if the binder has validation errors, {@code false}
+ * otherwise
+ */
+ public boolean hasValidationErrors() {
+ return hasValidationErrors;
+ }
+
+ @Override
+ public Binder<?> getSource() {
+ return (Binder<?>) super.getSource();
+ }
+
+ /**
+ * Gets the binder.
+ *
+ * @return the binder
+ */
+ public Binder<?> getBinder() {
+ return getSource();
+ }
+
+}
diff --git a/server/src/main/java/com/vaadin/data/StatusChangeListener.java b/server/src/main/java/com/vaadin/data/StatusChangeListener.java
new file mode 100644
index 0000000000..cb6afead24
--- /dev/null
+++ b/server/src/main/java/com/vaadin/data/StatusChangeListener.java
@@ -0,0 +1,37 @@
+/*
+ * 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.data;
+
+import java.io.Serializable;
+
+/**
+ * Listener interface for {@link StatusChangeEvent}s.
+ *
+ * @see StatusChangeEvent
+ * @author Vaadin Ltd
+ *
+ */
+@FunctionalInterface
+public interface StatusChangeListener extends Serializable {
+
+ /**
+ * Notifies the listener about status change {@code event}.
+ *
+ * @param event
+ * a status change event, not null
+ */
+ void statusChange(StatusChangeEvent event);
+}