diff options
author | Matti Hosio <mhosio@vaadin.com> | 2014-12-10 09:56:12 +0200 |
---|---|---|
committer | Matti Hosio <mhosio@vaadin.com> | 2014-12-10 16:00:51 +0200 |
commit | 5d6271489b2ce52f3c4eb3ce40cc1148c1092501 (patch) | |
tree | 885ecf2046b336b704b8b840c2a9c207e5db3d63 | |
parent | 409727ec617bfc092f584769997417c05259614d (diff) | |
download | vaadin-framework-5d6271489b2ce52f3c4eb3ce40cc1148c1092501.tar.gz vaadin-framework-5d6271489b2ce52f3c4eb3ce40cc1148c1092501.zip |
Declarative support for AbstractField (#7749)
Change-Id: I8eb917186886aa1a9c63939d2dfd1f59df973aa2
9 files changed, 372 insertions, 3 deletions
diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java index 1b7eeadd87..434fb114df 100644 --- a/server/src/com/vaadin/ui/AbstractComponent.java +++ b/server/src/com/vaadin/ui/AbstractComponent.java @@ -1206,7 +1206,7 @@ public abstract class AbstractComponent extends AbstractClientConnector private static final String[] customAttributes = new String[] { "width", "height", "debug-id", "error", "width-auto", "height-auto", "width-full", "height-full", "size-auto", "size-full", - "responsive", "immediate", "locale" }; + "responsive", "immediate", "locale", "read-only" }; /* * (non-Javadoc) diff --git a/server/src/com/vaadin/ui/AbstractField.java b/server/src/com/vaadin/ui/AbstractField.java index 369ad1253c..3728696233 100644 --- a/server/src/com/vaadin/ui/AbstractField.java +++ b/server/src/com/vaadin/ui/AbstractField.java @@ -25,6 +25,10 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; +import java.util.logging.Logger; + +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; import com.vaadin.data.Buffered; import com.vaadin.data.Property; @@ -43,6 +47,8 @@ import com.vaadin.server.CompositeErrorMessage; import com.vaadin.server.ErrorMessage; import com.vaadin.shared.AbstractFieldState; import com.vaadin.shared.util.SharedUtil; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; /** * <p> @@ -1751,4 +1757,63 @@ public abstract class AbstractField<T> extends AbstractComponent implements isListeningToPropertyEvents = false; } } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.ui.AbstractComponent#synchronizeFromDesign(org.jsoup.nodes + * .Element, com.vaadin.ui.declarative.DesignContext) + */ + @Override + public void synchronizeFromDesign(Element design, + DesignContext designContext) { + super.synchronizeFromDesign(design, designContext); + AbstractField def = designContext.getDefaultInstance(this.getClass()); + Attributes attr = design.attributes(); + boolean readOnly = DesignAttributeHandler.readAttribute("readonly", + attr, def.isReadOnly(), Boolean.class); + setReadOnly(readOnly); + // tabindex + int tabIndex = DesignAttributeHandler.readAttribute("tabindex", attr, + def.getTabIndex(), Integer.class); + setTabIndex(tabIndex); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.AbstractComponent#getCustomAttributes() + */ + @Override + protected Collection<String> getCustomAttributes() { + Collection<String> attributes = super.getCustomAttributes(); + attributes.add("readonly"); + attributes.add("tabindex"); + return attributes; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.ui.AbstractComponent#synchronizeToDesign(org.jsoup.nodes.Element + * , com.vaadin.ui.declarative.DesignContext) + */ + @Override + public void synchronizeToDesign(Element design, DesignContext designContext) { + super.synchronizeToDesign(design, designContext); + AbstractField def = designContext.getDefaultInstance(this.getClass()); + Attributes attr = design.attributes(); + // handle readonly + DesignAttributeHandler.writeAttribute("readonly", attr, + super.isReadOnly(), def.isReadOnly(), Boolean.class); + // handle tab index + DesignAttributeHandler.writeAttribute("tabindex", attr, getTabIndex(), + def.getTabIndex(), Integer.class); + } + + private static final Logger getLogger() { + return Logger.getLogger(AbstractField.class.getName()); + } } diff --git a/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java b/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java index f71b36f66c..1beddf57de 100644 --- a/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java +++ b/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java @@ -99,6 +99,7 @@ public class DesignAttributeHandler implements Serializable { * @param defaultInstance * the default instance of the class for fetching the default * values + * @return true on success */ public static boolean readAttribute(DesignSynchronizable component, String attribute, Attributes attributes, @@ -237,6 +238,69 @@ public class DesignAttributeHandler implements Serializable { } /** + * Reads the given attribute from attributes. If the attribute is not found, + * the provided default value is returned + * + * @param attribute + * the attribute key + * @param attributes + * the set of attributes to read from + * @param defaultValue + * the default value that is returned if the attribute is not + * found + * @param outputType + * the output type for the attribute + * @return the attribute value or the default value if the attribute is not + * found + */ + @SuppressWarnings("unchecked") + public static <T> T readAttribute(String attribute, Attributes attributes, + T defaultValue, Class<T> outputType) { + if (!isSupported(outputType)) { + throw new IllegalArgumentException("output type: " + + outputType.getName() + " not supported"); + } + if (!attributes.hasKey(attribute)) { + return defaultValue; + } else { + try { + String value = attributes.get(attribute); + return (T) fromAttributeValue(outputType, value); + } catch (Exception e) { + throw new DesignException("Failed to read attribute " + + attribute, e); + } + } + } + + /** + * Writes the given attribute value to attributes if it differs from the + * default attribute value + * + * @param attribute + * the attribute key + * @param attributes + * the set of attributes where the new attribute is written + * @param value + * the attribute value + * @param defaultValue + * the default attribute value + * @param the + * type of the input value + */ + public static <T> void writeAttribute(String attribute, + Attributes attributes, T value, T defaultValue, Class<T> inputType) { + if (!isSupported(inputType)) { + throw new IllegalArgumentException("input type: " + + inputType.getName() + " not supported"); + } + if (!SharedUtil.equals(value, defaultValue)) { + String attributeValue = toAttributeValue(value.getClass(), value); + attributes.put(attribute, attributeValue); + } + } + + /** * Formats the given design attribute value. The method is provided to * ensure consistent number formatting for design attribute values * diff --git a/server/tests/src/com/vaadin/tests/layoutparser/ParseAllSupportedComponentsTest.java b/server/tests/src/com/vaadin/tests/layoutparser/ParseAllSupportedComponentsTest.java new file mode 100644 index 0000000000..c3d1b36320 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/layoutparser/ParseAllSupportedComponentsTest.java @@ -0,0 +1,46 @@ +/* + * 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.layoutparser; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; + +import junit.framework.TestCase; + +import com.vaadin.ui.declarative.DesignContext; +import com.vaadin.ui.declarative.LayoutHandler; + +/** + * Just top level test case that contains all synchronizable components + * + * @author Vaadin Ltd + */ +public class ParseAllSupportedComponentsTest extends TestCase { + + public void testParsing() { + try { + DesignContext ctx = LayoutHandler + .parse(new FileInputStream( + "server/tests/src/com/vaadin/tests/layoutparser/all-components.html")); + assertNotNull("The returned design context can not be null", ctx); + assertNotNull("the component root can not be null", + ctx.getComponentRoot()); + } catch (FileNotFoundException e) { + e.printStackTrace(); + fail("Template parsing threw exception"); + } + } +} diff --git a/server/tests/src/com/vaadin/tests/layoutparser/all-components.html b/server/tests/src/com/vaadin/tests/layoutparser/all-components.html new file mode 100644 index 0000000000..99e516ff55 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/layoutparser/all-components.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<html> + <head> + <meta name="package-mapping" content="my:com.addon.mypackage"/> + </head> + <body> + <v-vertical-layout> + <!-- abstract component --> + <v-button primary-style-name="button" id="foo" style-name="red" caption="Some caption" icon="vaadin://themes/runo/icons/16/ok.png" description="My tooltip" error="Something went wrong" locale="en_US"></v-button> + + <!-- absolute layout --> + <v-absolute-layout> + <v-button :top="100px" :left="0px" :z-index=21>OK</v-button> + <v-button :bottom="0px" :right="0px">Cancel</v-button> + </v-absolute-layout> + + <!-- vertical layout --> + <v-vertical-layout spacing margin> + <v-button :top>OK</v-button> + <v-table size-full :expand=1 /> + </v-vertical-layout> + + <!-- horizontal layout --> + <v-horizontal-layout spacing margin> + <v-button :top>OK</v-button> + <v-table size-full :expand=1 /> + </v-horizontal-layout> + + <!-- form layout --> + <v-form-layout spacing margin> + <v-button :top>OK</v-button> + <v-table size-full :expand=1 /> + </v-form-layout> + + <!-- css layout --> + <v-css-layout> + <v-button>OK</v-button> + <v-table size-full /> + </v-css-layout> + + <!-- abstract field --> + <v-text-field buffered validation-visible=false invalid-committed invalid-allowed=false required required-error="This is a required field" conversion-error="Input {0} cannot be parsed" tabindex=3 readonly/> + + </v-vertical-layout> + </body> +</html>
\ No newline at end of file diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractcomponent/TestSynchronizeFromDesign.java b/server/tests/src/com/vaadin/tests/server/component/abstractcomponent/TestSynchronizeFromDesign.java index 06fc59a2f3..5153d92706 100644 --- a/server/tests/src/com/vaadin/tests/server/component/abstractcomponent/TestSynchronizeFromDesign.java +++ b/server/tests/src/com/vaadin/tests/server/component/abstractcomponent/TestSynchronizeFromDesign.java @@ -32,7 +32,7 @@ import com.vaadin.ui.Label; import com.vaadin.ui.declarative.DesignContext; /** - * Test case for reading the attributes of the abstract component from design + * Test case for reading the attributes of the AbstractComponent from design * * @author Vaadin Ltd */ diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractcomponent/TestSynchronizeToDesign.java b/server/tests/src/com/vaadin/tests/server/component/abstractcomponent/TestSynchronizeToDesign.java index cb3892f6da..d3bcc919ce 100644 --- a/server/tests/src/com/vaadin/tests/server/component/abstractcomponent/TestSynchronizeToDesign.java +++ b/server/tests/src/com/vaadin/tests/server/component/abstractcomponent/TestSynchronizeToDesign.java @@ -37,7 +37,7 @@ import com.vaadin.ui.HorizontalSplitPanel; import com.vaadin.ui.declarative.DesignContext; /** - * Test case for reading the attributes of the abstract component from design + * Test case for writing the attributes of the AbstractComponent to design * * @author Vaadin Ltd */ diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractfield/TestSynchronizeFromDesign.java b/server/tests/src/com/vaadin/tests/server/component/abstractfield/TestSynchronizeFromDesign.java new file mode 100644 index 0000000000..6a2c194978 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/abstractfield/TestSynchronizeFromDesign.java @@ -0,0 +1,71 @@ +/* + * 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.abstractfield; + +import junit.framework.TestCase; + +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; +import org.jsoup.parser.Tag; + +import com.vaadin.ui.AbstractField; +import com.vaadin.ui.TextField; +import com.vaadin.ui.declarative.DesignContext; + +/** + * + * Test case for reading the attributes of the AbstractField from design + * + * @author Vaadin Ltd + */ +public class TestSynchronizeFromDesign extends TestCase { + + private DesignContext ctx; + + @Override + protected void setUp() throws Exception { + super.setUp(); + ctx = new DesignContext(); + } + + public void testSynchronizeReadOnly() { + Element design = createDesign("readonly", ""); + AbstractField component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals(true, component.isReadOnly()); + design = createDesign("readonly", "false"); + component.synchronizeFromDesign(design, ctx); + assertEquals(false, component.isReadOnly()); + } + + public void testSynchronizeTabIndex() { + Element design = createDesign("tabindex", "2"); + AbstractField component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals("Tab index must be 2", 2, component.getTabIndex()); + } + + private AbstractField getComponent() { + return new TextField(); + } + + private Element createDesign(String key, String value) { + Attributes attributes = new Attributes(); + attributes.put(key, value); + Element node = new Element(Tag.valueOf("v-text-field"), "", attributes); + return node; + } +} diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractfield/TestSynchronizeToDesign.java b/server/tests/src/com/vaadin/tests/server/component/abstractfield/TestSynchronizeToDesign.java new file mode 100644 index 0000000000..26f64f9199 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/abstractfield/TestSynchronizeToDesign.java @@ -0,0 +1,77 @@ +/* + * 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.abstractfield; + +import junit.framework.TestCase; + +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; +import org.jsoup.parser.Tag; + +import com.vaadin.data.util.ObjectProperty; +import com.vaadin.ui.AbstractField; +import com.vaadin.ui.TextField; +import com.vaadin.ui.declarative.DesignContext; + +/** + * Test case for writing the attributes of the AbstractField to design + * + * @author Vaadin Ltd + */ +public class TestSynchronizeToDesign extends TestCase { + + private DesignContext ctx; + + @Override + protected void setUp() throws Exception { + super.setUp(); + ctx = new DesignContext(); + } + + public void testSynchronizeReadOnly() { + Element design = createDesign(); + AbstractField component = getComponent(); + component.setReadOnly(true); + component.synchronizeToDesign(design, ctx); + // we only changed one of the attributes, others are at default values + assertEquals(1, design.attributes().size()); + assertTrue("Design must contain readonly", design.hasAttr("readonly")); + assertTrue("Readonly must be true", design.attr("readonly").equals("") + || design.attr("readonly").equals("true")); + } + + public void testSynchronizeModelReadOnly() { + Element design = createDesign(); + AbstractField component = getComponent(); + ObjectProperty property = new ObjectProperty<String>("test"); + property.setReadOnly(true); + component.setPropertyDataSource(property); + component.synchronizeToDesign(design, ctx); + // make sure that property readonly is not written to design + assertFalse("Design must not contain readonly", + design.hasAttr("readonly")); + } + + private AbstractField getComponent() { + return new TextField(); + } + + private Element createDesign() { + Attributes attr = new Attributes(); + attr.put("should_be_removed", "foo"); + return new Element(Tag.valueOf("v-text-field"), "", attr); + } +} |