]> source.dussan.org Git - vaadin-framework.git/commitdiff
Modified the logic in setPropertyDatasource which determines if a new converter is...
authorArtur Signell <artur@vaadin.com>
Tue, 21 May 2013 13:51:32 +0000 (16:51 +0300)
committerVaadin Code Review <review@vaadin.com>
Wed, 22 May 2013 13:50:40 +0000 (13:50 +0000)
The previous logic had two flaws
* It allowed converter model type to be a sub type of the model type but not vice versa. Similarly for presentation type.
* If the user has set a converter it should be used and not be replaced unless it is absolutely sure that it cannot in any possible way handle conversion (e.g. converter from integer to double cannot handle string to list conversion). If there is a slight chance that it can handle conversion, let it be and let the user set another converter when needed.

Change-Id: I2e1c0b3aac90be63ddbc780195f8428398e28ada

server/src/com/vaadin/data/util/converter/ConverterUtil.java
server/src/com/vaadin/ui/AbstractField.java
server/src/com/vaadin/ui/Label.java
server/tests/src/com/vaadin/tests/data/converter/AnotherTestEnum.java [new file with mode: 0644]
server/tests/src/com/vaadin/tests/data/converter/TestAnyEnumToStringConverter.java [new file with mode: 0644]
server/tests/src/com/vaadin/tests/data/converter/TestEnum.java [new file with mode: 0644]
server/tests/src/com/vaadin/tests/data/converter/TestSpecificEnumToStringConverter.java [new file with mode: 0644]

index 61d155bc9a8080b56b58e61802d9dff21564c677..08d73630844a7174446d6ae97d8ab9210790eec0 100644 (file)
@@ -151,10 +151,14 @@ public class ConverterUtil implements Serializable {
 
     /**
      * Checks if the given converter can handle conversion between the given
-     * presentation and model type
+     * presentation and model type. Does strict type checking and only returns
+     * true if the converter claims it can handle exactly the given types.
+     * 
+     * @see #canConverterPossiblyHandle(Converter, Class, Class)
      * 
      * @param converter
-     *            The converter to check
+     *            The converter to check. If this is null the result is always
+     *            false.
      * @param presentationType
      *            The presentation type
      * @param modelType
@@ -168,10 +172,48 @@ public class ConverterUtil implements Serializable {
             return false;
         }
 
-        if (!modelType.isAssignableFrom(converter.getModelType())) {
+        if (modelType != converter.getModelType()) {
+            return false;
+        }
+        if (presentationType != converter.getPresentationType()) {
             return false;
         }
-        if (!presentationType.isAssignableFrom(converter.getPresentationType())) {
+
+        return true;
+    }
+
+    /**
+     * Checks if it possible that the given converter can handle conversion
+     * between the given presentation and model type somehow.
+     * 
+     * @param converter
+     *            The converter to check. If this is null the result is always
+     *            false.
+     * @param presentationType
+     *            The presentation type
+     * @param modelType
+     *            The model type
+     * @return true if the converter possibly support conversion between the
+     *         given presentation and model type, false otherwise
+     */
+    public static boolean canConverterPossiblyHandle(Converter<?, ?> converter,
+            Class<?> presentationType, Class<?> modelType) {
+        if (converter == null) {
+            return false;
+        }
+        Class<?> converterModelType = converter.getModelType();
+
+        if (!modelType.isAssignableFrom(converterModelType)
+                && !converterModelType.isAssignableFrom(modelType)) {
+            // model types are not compatible in any way
+            return false;
+        }
+
+        Class<?> converterPresentationType = converter.getPresentationType();
+        if (!presentationType.isAssignableFrom(converterPresentationType)
+                && !converterPresentationType
+                        .isAssignableFrom(presentationType)) {
+            // presentation types are not compatible in any way
             return false;
         }
 
index 3bca63a3b72848d3be7d4db7ca043583a8b3d4b3..606bf5fb213d05f115c62718cdbc907f82074eff 100644 (file)
@@ -616,17 +616,14 @@ public abstract class AbstractField<T> extends AbstractComponent implements
 
         // Check if the current converter is compatible.
         if (newDataSource != null
-                && !ConverterUtil.canConverterHandle(getConverter(), getType(),
-                        newDataSource.getType())) {
-            // Changing from e.g. Number -> Double should set a new converter,
-            // changing from Double -> Number can keep the old one (Property
-            // accepts Number)
-
-            // Set a new converter if there is a new data source and
-            // there is no old converter or the old is incompatible.
+                && !ConverterUtil.canConverterPossiblyHandle(getConverter(),
+                        getType(), newDataSource.getType())) {
+            // There is no converter set or there is no way the current
+            // converter can be compatible.
             setConverter(newDataSource.getType());
         }
-        // Gets the value from source
+        // Gets the value from source. This requires that a valid converter has
+        // been set.
         try {
             if (dataSource != null) {
                 T fieldValue = convertFromModel(getDataSourceValue());
index d037652a099800f0163645f86e8783d1cad76a6a..d7cee2a80d59ab13c4a960fc2421f17136733af9 100644 (file)
@@ -242,14 +242,17 @@ public class Label extends AbstractComponent implements Property<String>,
             ((Property.ValueChangeNotifier) dataSource).removeListener(this);
         }
 
+        // Check if the current converter is compatible.
         if (newDataSource != null
-                && !ConverterUtil.canConverterHandle(getConverter(),
-                        String.class, newDataSource.getType())) {
-            // Try to find a converter
+                && !ConverterUtil.canConverterPossiblyHandle(getConverter(),
+                        getType(), newDataSource.getType())) {
+            // There is no converter set or there is no way the current
+            // converter can be compatible.
             Converter<String, ?> c = ConverterUtil.getConverter(String.class,
                     newDataSource.getType(), getSession());
             setConverter(c);
         }
+
         dataSource = newDataSource;
         if (dataSource != null) {
             // Update the value from the data source. If data source was set to
diff --git a/server/tests/src/com/vaadin/tests/data/converter/AnotherTestEnum.java b/server/tests/src/com/vaadin/tests/data/converter/AnotherTestEnum.java
new file mode 100644 (file)
index 0000000..33a6a87
--- /dev/null
@@ -0,0 +1,16 @@
+package com.vaadin.tests.data.converter;
+
+public enum AnotherTestEnum {
+    ONE("ONE"), TWO("TWO");
+
+    private String id;
+
+    private AnotherTestEnum(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public String toString() {
+        return id;
+    }
+}
diff --git a/server/tests/src/com/vaadin/tests/data/converter/TestAnyEnumToStringConverter.java b/server/tests/src/com/vaadin/tests/data/converter/TestAnyEnumToStringConverter.java
new file mode 100644 (file)
index 0000000..baa81ce
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2000-2013 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.tests.data.converter;
+
+import java.util.Locale;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.vaadin.data.util.ObjectProperty;
+import com.vaadin.data.util.converter.Converter;
+import com.vaadin.data.util.converter.ReverseConverter;
+import com.vaadin.ui.TextField;
+
+public class TestAnyEnumToStringConverter {
+
+    public class AnyEnumToStringConverter implements Converter<Enum, String> {
+
+        private Class<? extends Enum>[] enumClass;
+
+        public AnyEnumToStringConverter(Class<? extends Enum>... enumClass) {
+            this.enumClass = enumClass;
+        }
+
+        @Override
+        public String convertToModel(Enum value, Locale locale)
+                throws com.vaadin.data.util.converter.Converter.ConversionException {
+            if (value == null) {
+                return null;
+            }
+
+            return value.toString();
+        }
+
+        @Override
+        public Enum convertToPresentation(String value, Locale locale)
+                throws com.vaadin.data.util.converter.Converter.ConversionException {
+            if (value == null) {
+                return null;
+            }
+            for (Class<? extends Enum> candidate : enumClass) {
+                for (Enum e : candidate.getEnumConstants()) {
+                    if (e.toString().equals(value)) {
+                        return e;
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        @Override
+        public Class<String> getModelType() {
+            return String.class;
+        }
+
+        @Override
+        public Class<Enum> getPresentationType() {
+            return Enum.class;
+        }
+
+    }
+
+    private AnyEnumToStringConverter converter;
+
+    @Before
+    public void setup() {
+        converter = new AnyEnumToStringConverter(TestEnum.class,
+                AnotherTestEnum.class);
+    }
+
+    @Test
+    public void nullConversion() {
+        Assert.assertEquals(null, converter.convertToModel(null, null));
+    }
+
+    @Test
+    public void enumToStringConversion() {
+        Assert.assertEquals(TestEnum.TWO.toString(),
+                converter.convertToModel(TestEnum.TWO, null));
+        Assert.assertEquals(AnotherTestEnum.TWO.toString(),
+                converter.convertToModel(AnotherTestEnum.TWO, null));
+    }
+
+    @Test
+    public void stringToEnumConversion() {
+        Assert.assertEquals(TestEnum.TWO,
+                converter.convertToPresentation(TestEnum.TWO.toString(), null));
+        Assert.assertEquals(AnotherTestEnum.TWO, converter
+                .convertToPresentation(AnotherTestEnum.TWO.toString(), null));
+    }
+
+    @Test
+    public void stringToEnumWithField() {
+        TextField tf = new TextField();
+        tf.setConverter(new ReverseConverter(converter));
+        tf.setPropertyDataSource(new ObjectProperty(AnotherTestEnum.TWO));
+        Assert.assertEquals(AnotherTestEnum.TWO.toString(), tf.getValue());
+        tf.setValue(AnotherTestEnum.ONE.toString());
+        Assert.assertEquals(AnotherTestEnum.ONE.toString(), tf.getValue());
+        Assert.assertEquals(AnotherTestEnum.ONE, tf.getConvertedValue());
+        Assert.assertEquals(AnotherTestEnum.ONE, tf.getPropertyDataSource()
+                .getValue());
+
+        tf.setPropertyDataSource(new ObjectProperty(TestEnum.TWO));
+        Assert.assertEquals(TestEnum.TWO.toString(), tf.getValue());
+        tf.setValue(TestEnum.ONE.toString());
+        Assert.assertEquals(TestEnum.ONE.toString(), tf.getValue());
+        Assert.assertEquals(TestEnum.ONE, tf.getConvertedValue());
+        Assert.assertEquals(TestEnum.ONE, tf.getPropertyDataSource().getValue());
+
+    }
+}
diff --git a/server/tests/src/com/vaadin/tests/data/converter/TestEnum.java b/server/tests/src/com/vaadin/tests/data/converter/TestEnum.java
new file mode 100644 (file)
index 0000000..a4b709a
--- /dev/null
@@ -0,0 +1,16 @@
+package com.vaadin.tests.data.converter;
+
+public enum TestEnum {
+    ONE("1"), TWO("2");
+
+    private String id;
+
+    private TestEnum(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public String toString() {
+        return id;
+    }
+}
diff --git a/server/tests/src/com/vaadin/tests/data/converter/TestSpecificEnumToStringConverter.java b/server/tests/src/com/vaadin/tests/data/converter/TestSpecificEnumToStringConverter.java
new file mode 100644 (file)
index 0000000..ef8c57f
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2000-2013 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.tests.data.converter;
+
+import java.util.Locale;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.vaadin.data.util.ObjectProperty;
+import com.vaadin.data.util.converter.Converter;
+import com.vaadin.data.util.converter.ReverseConverter;
+import com.vaadin.ui.TextField;
+
+public class TestSpecificEnumToStringConverter {
+
+    public class SpecificEnumToStringConverter implements
+            Converter<Enum, String> {
+
+        private Class<? extends Enum> enumClass;
+
+        public SpecificEnumToStringConverter(Class<? extends Enum> enumClass) {
+            this.enumClass = enumClass;
+        }
+
+        @Override
+        public String convertToModel(Enum value, Locale locale)
+                throws com.vaadin.data.util.converter.Converter.ConversionException {
+            if (value == null) {
+                return null;
+            }
+
+            return value.toString();
+        }
+
+        @Override
+        public Enum convertToPresentation(String value, Locale locale)
+                throws com.vaadin.data.util.converter.Converter.ConversionException {
+            if (value == null) {
+                return null;
+            }
+
+            for (Enum e : enumClass.getEnumConstants()) {
+                if (e.toString().equals(value)) {
+                    return e;
+                }
+            }
+
+            return null;
+        }
+
+        @Override
+        public Class<String> getModelType() {
+            return String.class;
+        }
+
+        @Override
+        public Class<Enum> getPresentationType() {
+            return (Class<Enum>) enumClass;
+        }
+
+    }
+
+    SpecificEnumToStringConverter testEnumConverter;
+    SpecificEnumToStringConverter anotherTestEnumConverter;
+
+    @Before
+    public void setup() {
+        testEnumConverter = new SpecificEnumToStringConverter(TestEnum.class);
+        anotherTestEnumConverter = new SpecificEnumToStringConverter(
+                AnotherTestEnum.class);
+    }
+
+    @Test
+    public void nullConversion() {
+        Assert.assertEquals(null, testEnumConverter.convertToModel(null, null));
+    }
+
+    @Test
+    public void enumToStringConversion() {
+        Assert.assertEquals(TestEnum.TWO.toString(),
+                testEnumConverter.convertToModel(TestEnum.TWO, null));
+    }
+
+    @Test
+    public void stringToEnumConversion() {
+        Assert.assertEquals(TestEnum.TWO, testEnumConverter
+                .convertToPresentation(TestEnum.TWO.toString(), null));
+    }
+
+    @Test
+    public void stringToEnumWithField() {
+        TextField tf = new TextField();
+        tf.setConverter(new ReverseConverter(anotherTestEnumConverter));
+        tf.setPropertyDataSource(new ObjectProperty(AnotherTestEnum.TWO));
+        Assert.assertEquals(AnotherTestEnum.TWO.toString(), tf.getValue());
+        tf.setValue(AnotherTestEnum.ONE.toString());
+        Assert.assertEquals(AnotherTestEnum.ONE.toString(), tf.getValue());
+        Assert.assertEquals(AnotherTestEnum.ONE, tf.getConvertedValue());
+        Assert.assertEquals(AnotherTestEnum.ONE, tf.getPropertyDataSource()
+                .getValue());
+
+    }
+}