aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIlia Motornyi <elmot@vaadin.com>2018-03-20 18:30:28 +0200
committerGitHub <noreply@github.com>2018-03-20 18:30:28 +0200
commit940a1ef584c82e343474863408771ff091044bdc (patch)
tree9fe6ab321f7cf6f298a09b72409550f236def7e7
parent4b1dd9f54cff3d7768e672ea42b36bbfa3ff60c4 (diff)
downloadvaadin-framework-940a1ef584c82e343474863408771ff091044bdc.tar.gz
vaadin-framework-940a1ef584c82e343474863408771ff091044bdc.zip
Implement ReadOnlyHasValue helper (#10643)
-rw-r--r--documentation/datamodel/datamodel-forms.asciidoc12
-rw-r--r--server/src/main/java/com/vaadin/data/ReadOnlyHasValue.java119
-rw-r--r--server/src/test/java/com/vaadin/data/ReadOnlyHasValueTest.java116
3 files changed, 247 insertions, 0 deletions
diff --git a/documentation/datamodel/datamodel-forms.asciidoc b/documentation/datamodel/datamodel-forms.asciidoc
index f307144a57..5be66f81ea 100644
--- a/documentation/datamodel/datamodel-forms.asciidoc
+++ b/documentation/datamodel/datamodel-forms.asciidoc
@@ -91,6 +91,18 @@ binder.bind(nameField,
}
});
----
+== Binding non-modifiable Data
+
+Non-modifiable data can be also bound to any component or component property with [classname]#ReadOnlyHasValue# helper class.
+For example, `Panel` caption can display a person full name:
+
+[source, java]
+----
+Panel infoPanel = new Panel();
+ReadOnlyHasValue<Person> panelTitle = new ReadOnlyHasValue<>(
+ person -> infoPanel.setCaption(person.getLastName() + ", " + person.getFirstName()));
+binder.forField(panelTitle).bind(person -> person, null);
+----
== Validating and Converting User Input
diff --git a/server/src/main/java/com/vaadin/data/ReadOnlyHasValue.java b/server/src/main/java/com/vaadin/data/ReadOnlyHasValue.java
new file mode 100644
index 0000000000..f3f4f54151
--- /dev/null
+++ b/server/src/main/java/com/vaadin/data/ReadOnlyHasValue.java
@@ -0,0 +1,119 @@
+/*
+ * 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 com.vaadin.server.SerializableConsumer;
+import com.vaadin.shared.Registration;
+import com.vaadin.ui.Label;
+
+import java.io.Serializable;
+import java.util.LinkedHashSet;
+import java.util.Objects;
+
+/**
+ * Generic {@link HasValue} to use any type of component with Vaadin data binding.
+ * <p>
+ * Example:
+ * <pre>
+ * Label label = new Label();
+ * ReadOnlyHasValue&lt;String&gt; hasValue = new ReadOnlyHasValue&lt;&gt;(label::setCaption);
+ * binder.forField(hasValue).bind(SomeBean::getName);
+ * </pre>
+ *
+ * @param <V> the value type
+ * @since
+ */
+public class ReadOnlyHasValue<V> implements HasValue<V>, Serializable {
+ private V value;
+ private final SerializableConsumer<V> valueProcessor;
+ private final V emptyValue;
+ private LinkedHashSet<ValueChangeListener<V>> listenerList;
+
+ /**
+ * Creates new {@code ReadOnlyHasValue}
+ *
+ * @param valueProcessor the value valueProcessor, e.g. {@link Label#setValue}
+ * @param emptyValue the value to be used as empty, {@code null} by default
+ */
+ public ReadOnlyHasValue(SerializableConsumer<V> valueProcessor, V emptyValue) {
+ this.valueProcessor = valueProcessor;
+ this.emptyValue = emptyValue;
+ }
+
+ /**
+ * Creates new {@code ReadOnlyHasValue} with {@code null} as an empty value.
+ *
+ * @param valueProcessor the value valueProcessor, e.g. {@link Label#setValue}
+ */
+ public ReadOnlyHasValue(SerializableConsumer<V> valueProcessor) {
+ this(valueProcessor,null);
+ }
+
+ @Override
+ public void setValue(V value) {
+ V oldValue = this.value;
+ this.value = value;
+ valueProcessor.accept(value);
+ if (listenerList != null && ! Objects.equals(oldValue, value)) {
+ for (ValueChangeListener<V> valueChangeListener : listenerList) {
+ valueChangeListener.valueChange(
+ new ValueChangeEvent<>(null, this, oldValue, false));
+ }
+ }
+ }
+
+ @Override
+ public V getValue() {
+ return value;
+ }
+
+ @Override
+ public Registration addValueChangeListener(
+ ValueChangeListener<V> listener) {
+ Objects.requireNonNull(listener, "Listener must not be null.");
+ if (listenerList == null) {
+ listenerList = new LinkedHashSet<>();
+ }
+ listenerList.add(listener);
+
+ return () -> listenerList.remove(listener);
+ }
+
+ @Override
+ public boolean isRequiredIndicatorVisible() {
+ return false;
+ }
+
+ @Override
+ public void setRequiredIndicatorVisible(boolean requiredIndicatorVisible) {
+ if (requiredIndicatorVisible) throw new IllegalArgumentException("Not Writable");
+ }
+
+ @Override
+ public void setReadOnly(boolean readOnly) {
+ if (!readOnly) throw new IllegalArgumentException("Not Writable");
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return true;
+ }
+
+ @Override
+ public V getEmptyValue() {
+ return emptyValue;
+ }
+}
diff --git a/server/src/test/java/com/vaadin/data/ReadOnlyHasValueTest.java b/server/src/test/java/com/vaadin/data/ReadOnlyHasValueTest.java
new file mode 100644
index 0000000000..38338e8a22
--- /dev/null
+++ b/server/src/test/java/com/vaadin/data/ReadOnlyHasValueTest.java
@@ -0,0 +1,116 @@
+package com.vaadin.data;
+
+import com.vaadin.shared.Registration;
+import com.vaadin.ui.Label;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Objects;
+
+import static org.junit.Assert.*;
+
+public class ReadOnlyHasValueTest {
+ private static final String SAY_SOMETHING = "Say something";
+ private static final String SAY_SOMETHING_ELSE = "Say something else";
+ private static final String NO_VALUE = "-no-value-";
+ private Label label;
+ private ReadOnlyHasValue<String> hasValue;
+
+ @Before
+ public void setup() {
+ label = new Label();
+ hasValue = new ReadOnlyHasValue<>(label::setCaption);
+ }
+
+ @Test
+ public void testBase() {
+ hasValue.setReadOnly(true);
+ hasValue.setRequiredIndicatorVisible(false);
+ Registration registration = hasValue.addValueChangeListener(e -> {
+ });
+ registration.remove();
+ hasValue.setValue(SAY_SOMETHING);
+ assertEquals(SAY_SOMETHING, hasValue.getValue());
+ assertEquals(SAY_SOMETHING, label.getCaption());
+ hasValue.setValue(SAY_SOMETHING_ELSE);
+ assertEquals(SAY_SOMETHING_ELSE, hasValue.getValue());
+ assertEquals(SAY_SOMETHING_ELSE, label.getCaption());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testRO() {
+ hasValue.setReadOnly(false);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testIndicator() {
+ hasValue.setRequiredIndicatorVisible(true);
+ }
+
+ @Test
+ public void testBind() {
+ Binder<Bean> beanBinder = new Binder<>(Bean.class);
+ Label label = new Label();
+ ReadOnlyHasValue<Long> intHasValue = new ReadOnlyHasValue<>(
+ i -> label.setValue(Objects.toString(i, "")));
+
+ beanBinder.forField(intHasValue).bind("v");
+
+ beanBinder.readBean(new Bean(42));
+ assertEquals("42", label.getValue());
+ assertEquals(42L, intHasValue.getValue().longValue());
+
+ Registration registration = intHasValue.addValueChangeListener(e -> {
+ assertEquals(42L, e.getOldValue().longValue());
+ assertSame(intHasValue, e.getSource());
+ assertSame(null, e.getComponent());
+ assertSame(null, e.getComponent());
+ assertFalse(e.isUserOriginated());
+ });
+ beanBinder.readBean(new Bean(1984));
+ assertEquals("1984", label.getValue());
+ assertEquals(1984L, intHasValue.getValue().longValue());
+
+ registration.remove();
+
+ beanBinder.readBean(null);
+ assertEquals("", label.getValue());
+ assertEquals(null, intHasValue.getValue());
+
+ }
+
+ @Test
+ public void testEmptyValue() {
+ Binder<Bean> beanBinder = new Binder<>(Bean.class);
+ Label label = new Label();
+ ReadOnlyHasValue<String> strHasValue =
+ new ReadOnlyHasValue<>(label::setValue, NO_VALUE);
+
+ beanBinder.forField(strHasValue)
+ .withConverter(Long::parseLong,(Long i)->"" + i)
+ .bind("v");
+
+ beanBinder.readBean(new Bean(42));
+ assertEquals("42", label.getValue());
+
+ beanBinder.readBean(null);
+ assertEquals(NO_VALUE, label.getValue());
+ assertTrue(strHasValue.isEmpty());
+ }
+
+ public static class Bean {
+ public Bean(long v) {
+ this.v = v;
+ }
+
+ private long v;
+
+ public long getV() {
+ return v;
+ }
+
+ public void setV(long v) {
+ this.v = v;
+ }
+ }
+}