diff options
7 files changed, 409 insertions, 4 deletions
diff --git a/server/src/com/vaadin/ui/AbstractField.java b/server/src/com/vaadin/ui/AbstractField.java index 9b9c7efd86..5c02c9e5fb 100644 --- a/server/src/com/vaadin/ui/AbstractField.java +++ b/server/src/com/vaadin/ui/AbstractField.java @@ -1772,6 +1772,7 @@ public abstract class AbstractField<T> extends AbstractComponent implements attributes.add("readonly"); // must be handled by subclasses attributes.add("value"); + attributes.add("converted-value"); return attributes; } diff --git a/server/src/com/vaadin/ui/AbstractSelect.java b/server/src/com/vaadin/ui/AbstractSelect.java index 423ebcb46a..06790ca78d 100644 --- a/server/src/com/vaadin/ui/AbstractSelect.java +++ b/server/src/com/vaadin/ui/AbstractSelect.java @@ -29,6 +29,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.jsoup.nodes.Element; + import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.data.Property; @@ -49,6 +51,9 @@ import com.vaadin.server.PaintTarget; import com.vaadin.server.Resource; import com.vaadin.shared.ui.combobox.FilteringMode; import com.vaadin.shared.ui.dd.VerticalDropLocation; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; +import com.vaadin.ui.declarative.DesignException; /** * <p> @@ -2181,4 +2186,40 @@ public abstract class AbstractSelect extends AbstractField<Object> implements public String generateDescription(Component source, Object itemId, Object propertyId); } -} + + @Override + public void readDesign(Element design, DesignContext designContext) { + // handle default attributes + super.readDesign(design, designContext); + // handle children specifying selectable items (<option>) + Set<String> selected = new HashSet<String>(); + for (Element child : design.children()) { + if (!"option".equals(child.nodeName())) { + throw new DesignException( + "Unsupported child element in a select: " + + child.nodeName() + "."); + } + String itemId = child.html(); + addItem(itemId); + if (child.hasAttr("icon")) { + setItemIcon( + itemId, + DesignAttributeHandler.readAttribute("icon", + child.attributes(), Resource.class)); + } + if (child.hasAttr("selected")) { + selected.add(itemId); + } + } + if (!selected.isEmpty()) { + if (isMultiSelect()) { + setValue(selected); + } else if (selected.size() == 1) { + setValue(selected.iterator().next()); + } else { + throw new DesignException( + "Multiple values selected for a single select component"); + } + } + } +}
\ No newline at end of file diff --git a/server/src/com/vaadin/ui/declarative/DesignFormatter.java b/server/src/com/vaadin/ui/declarative/DesignFormatter.java index 25cf05cc9b..985b9235f3 100644 --- a/server/src/com/vaadin/ui/declarative/DesignFormatter.java +++ b/server/src/com/vaadin/ui/declarative/DesignFormatter.java @@ -33,6 +33,7 @@ import com.vaadin.server.Resource; import com.vaadin.ui.declarative.converters.DesignDateConverter; import com.vaadin.ui.declarative.converters.DesignEnumConverter; import com.vaadin.ui.declarative.converters.DesignFormatConverter; +import com.vaadin.ui.declarative.converters.DesignObjectConverter; import com.vaadin.ui.declarative.converters.DesignResourceConverter; import com.vaadin.ui.declarative.converters.DesignShortcutActionConverter; import com.vaadin.ui.declarative.converters.DesignTimeZoneConverter; @@ -50,6 +51,7 @@ public class DesignFormatter implements Serializable { private final Map<Class<?>, Converter<String, ?>> converterMap = new ConcurrentHashMap<Class<?>, Converter<String, ?>>(); private final Converter<String, Enum> stringEnumConverter = new DesignEnumConverter(); + private final Converter<String, Object> stringObjectConverter = new DesignObjectConverter(); /** * Creates the formatter with default types already mapped. @@ -294,6 +296,12 @@ public class DesignFormatter implements Serializable { @SuppressWarnings("unchecked") protected <T> Converter<String, T> findConverterFor( Class<? extends T> sourceType, boolean strict) { + if (sourceType == Object.class) { + // Use for propertyIds, itemIds and such. Only string type objects + // are really supported if no special logic is implemented in the + // component. + return (Converter<String, T>) stringObjectConverter; + } if (sourceType.isEnum()) { return (Converter<String, T>) stringEnumConverter; } else if (converterMap.containsKey(sourceType)) { diff --git a/server/src/com/vaadin/ui/declarative/converters/DesignObjectConverter.java b/server/src/com/vaadin/ui/declarative/converters/DesignObjectConverter.java new file mode 100644 index 0000000000..f11585d6b8 --- /dev/null +++ b/server/src/com/vaadin/ui/declarative/converters/DesignObjectConverter.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2014 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.declarative.converters; + +import java.util.Locale; + +import com.vaadin.data.util.converter.Converter; +import com.vaadin.ui.declarative.DesignAttributeHandler; + +/** + * An converter for Object to/from String for {@link DesignAttributeHandler} to + * use internally. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class DesignObjectConverter implements Converter<String, Object> { + + @Override + public Object convertToModel(String value, + Class<? extends Object> targetType, Locale locale) + throws com.vaadin.data.util.converter.Converter.ConversionException { + return value; + } + + @Override + public String convertToPresentation(Object value, + Class<? extends String> targetType, Locale locale) + throws com.vaadin.data.util.converter.Converter.ConversionException { + if (value == null) { + return null; + } + + return value.toString(); + } + + @Override + public Class<Object> getModelType() { + return Object.class; + } + + @Override + public Class<String> getPresentationType() { + return String.class; + } + +} diff --git a/server/tests/src/com/vaadin/tests/design/DeclarativeTestBaseBase.java b/server/tests/src/com/vaadin/tests/design/DeclarativeTestBaseBase.java index 653e1b1cb9..1874c3ec95 100644 --- a/server/tests/src/com/vaadin/tests/design/DeclarativeTestBaseBase.java +++ b/server/tests/src/com/vaadin/tests/design/DeclarativeTestBaseBase.java @@ -26,6 +26,8 @@ import java.util.List; import org.jsoup.Jsoup; import org.jsoup.nodes.Attribute; import org.jsoup.nodes.Element; +import org.jsoup.nodes.Node; +import org.jsoup.nodes.TextNode; import org.junit.Assert; import com.vaadin.ui.Component; @@ -153,11 +155,15 @@ public abstract class DeclarativeTestBaseBase<T extends Component> { .append(producedElem.attr(attrName)).append("\'"); } sb.append(">"); - for (Element child : producedElem.children()) { - elementToHtml(child, sb); + for (Node child : producedElem.childNodes()) { + if (child instanceof Element) { + elementToHtml((Element) child, sb); + } else if (child instanceof TextNode) { + String text = ((TextNode) child).text(); + sb.append(text.trim()); + } } sb.append("</").append(producedElem.tagName()).append(">"); return sb.toString(); } - } diff --git a/server/tests/src/com/vaadin/tests/design/all-components.html b/server/tests/src/com/vaadin/tests/design/all-components.html index 0f16ea9363..39aecb6db1 100644 --- a/server/tests/src/com/vaadin/tests/design/all-components.html +++ b/server/tests/src/com/vaadin/tests/design/all-components.html @@ -105,6 +105,18 @@ <!-- checkbox --> <v-check-box checked/> + + <!-- abstract select --> + <v-list-select new-items-allowed multi-select + item-caption-mode="index" + null-selection-allowed=false> + </v-list-select> + + <v-combo-box> + <option icon="http://something/my-icon.png">First value</option> + <option>Second value</option> + </v-combo-box> + </v-vertical-layout> </body> </html> diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractselect/AbstractSelectDeclarativeTest.java b/server/tests/src/com/vaadin/tests/server/component/abstractselect/AbstractSelectDeclarativeTest.java new file mode 100644 index 0000000000..1f5c90516d --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/abstractselect/AbstractSelectDeclarativeTest.java @@ -0,0 +1,277 @@ +/* + * Copyright 2000-2014 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.server.component.abstractselect; + +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; +import org.jsoup.parser.Tag; +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.Container; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.server.ExternalResource; +import com.vaadin.server.Resource; +import com.vaadin.tests.design.DeclarativeTestBase; +import com.vaadin.ui.AbstractSelect; +import com.vaadin.ui.AbstractSelect.ItemCaptionMode; +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.ListSelect; +import com.vaadin.ui.declarative.DesignContext; +import com.vaadin.ui.declarative.DesignException; + +/** + * Test cases for reading the properties of selection components. + * + * @author Vaadin Ltd + */ +public class AbstractSelectDeclarativeTest extends + DeclarativeTestBase<AbstractSelect> { + + public String getDesignSingleSelectNewItemsAllowed() { + return "<v-combo-box new-items-allowed='true' item-caption-mode='icon_only'" + + " null-selection-item-id='nullIid'/>"; + + } + + public AbstractSelect getExpectedSingleSelectNewItemsAllowed() { + ComboBox c = new ComboBox(); + c.setNewItemsAllowed(true); + c.setItemCaptionMode(ItemCaptionMode.ICON_ONLY); + c.setNullSelectionAllowed(true);// Default + c.setNullSelectionItemId("nullIid"); + return c; + } + + public String getDesignMultiSelect() { + return "<v-list-select multi-select='true' null-selection-allowed='false' new-items-allowed='true' item-caption-mode='property' />"; + } + + public AbstractSelect getExpectedMultiSelect() { + ListSelect c = new ListSelect(); + c.setNewItemsAllowed(true); + c.setNullSelectionAllowed(false); + c.setItemCaptionMode(ItemCaptionMode.PROPERTY); + c.setMultiSelect(true); + return c; + } + + @Test + public void testReadSingleSelectNewItemsAllowed() { + testRead(getDesignSingleSelectNewItemsAllowed(), + getExpectedSingleSelectNewItemsAllowed()); + } + + @Test + public void testWriteSingleSelectNewItemsAllowed() { + testWrite(getDesignSingleSelectNewItemsAllowed(), + getExpectedSingleSelectNewItemsAllowed()); + } + + @Test + public void testReadMultiSelect() { + testRead(getDesignMultiSelect(), getExpectedMultiSelect()); + } + + @Test + public void testWriteMultiSelect() { + testWrite(getDesignMultiSelect(), getExpectedMultiSelect()); + } + + @Test + public void testReadInlineData() { + testRead(getDesignForInlineData(), getExpectedComponentForInlineData()); + } + + @Test(expected = DesignException.class) + public void testReadMultipleValuesForSingleSelect() { + testRead("<v-list-select>" + "<option selected>1</option>" + + "<option selected>2</option>" + "</v-list-select>", null); + } + + @Test + public void testReadMultipleValuesForMultiSelect() { + ListSelect ls = new ListSelect(); + ls.setMultiSelect(true); + ls.addItem("1"); + ls.addItem("2"); + ls.select("1"); + ls.select("2"); + testRead("<v-list-select multi-select>" + "<option selected>1</option>" + + "<option selected>2</option>" + "</v-list-select>", ls); + } + + @Test + public void testReadSingleValueForMultiSelect() { + ListSelect ls = new ListSelect(); + ls.setMultiSelect(true); + ls.addItem("1"); + ls.addItem("2"); + ls.select("1"); + testRead("<v-list-select multi-select>" + "<option selected>1</option>" + + "<option>2</option>" + "</v-list-select>", ls); + } + + @Test + public void testReadSingleValueForSingleSelect() { + ListSelect ls = new ListSelect(); + ls.setMultiSelect(false); + ls.addItem("1"); + ls.addItem("2"); + ls.select("1"); + testRead("<v-list-select>" + "<option selected>1</option>" + + "<option>2</option>" + "</v-list-select>", ls); + } + + @Test + public void testWriteInlineData() { + String modifiedDesign = getDesignForInlineData(); + // No data is written by default + modifiedDesign = modifiedDesign.replaceAll( + "[ \n]*<option(.*)</option>[ \n]*", ""); + testWrite(modifiedDesign, getExpectedComponentForInlineData()); + } + + private String getDesignForInlineData() { + return "<v-list-select>\n" + + " <option icon='http://some.url/icon.png'>Value 1</option>\n" // + + " <option selected>Value 2</option>\n"// + + "</v-list-select>"; + } + + private AbstractSelect getExpectedComponentForInlineData() { + AbstractSelect as = new ListSelect(); + as.addItem("Value 1"); + as.setItemIcon("Value 1", new ExternalResource( + "http://some.url/icon.png")); + as.addItem("Value 2"); + as.setValue("Value 2"); + return as; + } + + @Test + public void testReadAttributesSingleSelect() { + Element design = createDesignWithAttributesSingleSelect(); + ComboBox cb = new ComboBox(); + IndexedContainer container = new IndexedContainer(); + container.addContainerProperty("icon", Resource.class, null); + container.addContainerProperty("name", String.class, null); + cb.setContainerDataSource(container); + cb.readDesign(design, new DesignContext()); + Assert.assertTrue("Adding new items should be allowed.", + cb.isNewItemsAllowed()); + assertEquals("Wrong item caption mode.", + AbstractSelect.ItemCaptionMode.PROPERTY, + cb.getItemCaptionMode()); + assertEquals("Wrong item caption property id.", "name", + cb.getItemCaptionPropertyId()); + assertEquals("Wrong item icon property id.", "icon", + cb.getItemIconPropertyId()); + Assert.assertTrue("Null selection should be allowed.", + cb.isNullSelectionAllowed()); + assertEquals("Wrong null selection item id.", "No items selected", + cb.getNullSelectionItemId()); + } + + @Test + public void testReadAttributesMultiSelect() { + Element design = createDesignWithAttributesMultiSelect(); + ListSelect ls = new ListSelect(); + ls.readDesign(design, new DesignContext()); + Assert.assertTrue("Multi select should be allowed.", ls.isMultiSelect()); + assertEquals("Wrong caption mode.", + AbstractSelect.ItemCaptionMode.EXPLICIT, + ls.getItemCaptionMode()); + Assert.assertFalse("Null selection should not be allowed.", + ls.isNullSelectionAllowed()); + } + + private Element createDesignWithAttributesSingleSelect() { + Attributes attributes = new Attributes(); + attributes.put("new-items-allowed", ""); + attributes.put("multi-select", "false"); + attributes.put("item-caption-mode", "property"); + attributes.put("item-caption-property-id", "name"); + attributes.put("item-icon-property-id", "icon"); + attributes.put("null-selection-allowed", "true"); + attributes.put("null-selection-item-id", "No items selected"); + return new Element(Tag.valueOf("v-combo-box"), "", attributes); + } + + private Element createDesignWithAttributesMultiSelect() { + Attributes attributes = new Attributes(); + attributes.put("multi-select", ""); + attributes.put("item-caption-mode", "EXPLICIT"); + attributes.put("null-selection-allowed", "false"); + return new Element(Tag.valueOf("v-list-select"), "", attributes); + } + + @Test + public void testWriteAttributesSingleSelect() { + ComboBox cb = createSingleSelectWithOnlyAttributes(); + Element e = new Element(Tag.valueOf("v-combo-box"), ""); + cb.writeDesign(e, new DesignContext()); + assertEquals("Wrong caption for the combo box.", "A combo box", + e.attr("caption")); + Assert.assertTrue("Adding new items should be allowed.", + "true".equals(e.attr("new-items-allowed"))); + assertEquals("Wrong item caption mode.", "icon_only", + e.attr("item-caption-mode")); + assertEquals("Wrong item icon property id.", "icon", + e.attr("item-icon-property-id")); + Assert.assertTrue( + "Null selection should be allowed.", + "".equals(e.attr("null-selection-allowed")) + || "true".equals(e.attr("null-selection-allowed"))); + assertEquals("Wrong null selection item id.", "No item selected", + e.attr("null-selection-item-id")); + } + + @Test + public void testWriteMultiListSelect() { + ListSelect ls = createMultiSelect(); + Element e = new Element(Tag.valueOf("v-list-select"), ""); + ls.writeDesign(e, new DesignContext()); + assertEquals("Null selection should not be allowed.", "false", + e.attr("null-selection-allowed")); + Assert.assertTrue( + "Multi select should be allowed.", + "".equals(e.attr("multi-select")) + || "true".equals(e.attr("multi-select"))); + } + + public ComboBox createSingleSelectWithOnlyAttributes() { + ComboBox cb = new ComboBox(); + Container dataSource = new IndexedContainer(); + dataSource.addContainerProperty("icon", Resource.class, null); + cb.setContainerDataSource(dataSource); + cb.setCaption("A combo box"); + cb.setNewItemsAllowed(true); + cb.setItemCaptionMode(ItemCaptionMode.ICON_ONLY); + cb.setItemIconPropertyId("icon"); + cb.setNullSelectionAllowed(true); + cb.setNullSelectionItemId("No item selected"); + return cb; + } + + public ListSelect createMultiSelect() { + ListSelect ls = new ListSelect(); + ls.setNullSelectionAllowed(false); + ls.setMultiSelect(true); + return ls; + } + +}
\ No newline at end of file |