diff options
author | Artur Signell <artur@vaadin.com> | 2014-12-15 23:02:22 +0200 |
---|---|---|
committer | Artur Signell <artur@vaadin.com> | 2014-12-15 23:04:41 +0200 |
commit | 353206974a542d0aadc278dea4adeff226fb3a9e (patch) | |
tree | bd6511b2d62cefaaa88f5131abf69f11a1cd12e5 /server | |
parent | 8eafe7aee31b3e2f80c87b39c4e42e260a86a5eb (diff) | |
download | vaadin-framework-353206974a542d0aadc278dea4adeff226fb3a9e.tar.gz vaadin-framework-353206974a542d0aadc278dea4adeff226fb3a9e.zip |
Fix writing issues (#7749)
* A root component with @DesignRoot must always use its superclass for
default values. Otherwise the written design will be empty as the
design is read in the constructor.
* A component which is not the root must not write its component tree
if the component tree is generated in the constructor. This is a
simplification which should be good enough for most cases (can't add
children in constructor and also using addComponent - in this case
the component added with addComponent will not be written).
* Test cases for nested templates
Change-Id: I3a384d1d8654b9865a3a790ebeb055a300a62135
Diffstat (limited to 'server')
19 files changed, 374 insertions, 25 deletions
diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java index 0b9f8bd244..83833b75ce 100644 --- a/server/src/com/vaadin/ui/AbstractComponent.java +++ b/server/src/com/vaadin/ui/AbstractComponent.java @@ -1206,8 +1206,7 @@ public abstract class AbstractComponent extends AbstractClientConnector public void writeDesign(Element design, DesignContext designContext) { // clear element contents DesignAttributeHandler.clearElement(design); - AbstractComponent def = designContext.getDefaultInstance(this - .getClass()); + AbstractComponent def = designContext.getDefaultInstance(this); Attributes attr = design.attributes(); // handle default attributes for (String attribute : getDefaultAttributes()) { diff --git a/server/src/com/vaadin/ui/AbstractField.java b/server/src/com/vaadin/ui/AbstractField.java index c3e2036936..e40bdbde68 100644 --- a/server/src/com/vaadin/ui/AbstractField.java +++ b/server/src/com/vaadin/ui/AbstractField.java @@ -1803,7 +1803,7 @@ public abstract class AbstractField<T> extends AbstractComponent implements @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); - AbstractField def = designContext.getDefaultInstance(this.getClass()); + AbstractField def = designContext.getDefaultInstance(this); Attributes attr = design.attributes(); // handle readonly DesignAttributeHandler.writeAttribute("readonly", attr, diff --git a/server/src/com/vaadin/ui/AbstractOrderedLayout.java b/server/src/com/vaadin/ui/AbstractOrderedLayout.java index cd5609f091..b105d53fce 100644 --- a/server/src/com/vaadin/ui/AbstractOrderedLayout.java +++ b/server/src/com/vaadin/ui/AbstractOrderedLayout.java @@ -533,13 +533,17 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements // synchronize default attributes super.writeDesign(design, designContext); // handle margin - AbstractOrderedLayout def = designContext.getDefaultInstance(this - .getClass()); + AbstractOrderedLayout def = (AbstractOrderedLayout) designContext + .getDefaultInstance(this); if (getMargin().getBitMask() != def.getMargin().getBitMask()) { design.attr("margin", ""); } // handle children Element designElement = design; + if (!designContext.shouldWriteChildren(this, def)) { + return; + } + for (Component child : this) { Element childNode = designContext.createNode(child); designElement.appendChild(childNode); diff --git a/server/src/com/vaadin/ui/AbstractSplitPanel.java b/server/src/com/vaadin/ui/AbstractSplitPanel.java index 9c33665c1c..4c85311075 100644 --- a/server/src/com/vaadin/ui/AbstractSplitPanel.java +++ b/server/src/com/vaadin/ui/AbstractSplitPanel.java @@ -627,8 +627,8 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { super.writeDesign(design, designContext); // handle custom attributes (write only if a value is not the // default value) - AbstractSplitPanel def = designContext.getDefaultInstance(this - .getClass()); + AbstractSplitPanel def = (AbstractSplitPanel) designContext + .getDefaultInstance(this); if (getSplitPosition() != def.getSplitPosition() || !def.getSplitPositionUnit().equals(getSplitPositionUnit())) { String splitPositionString = asString(getSplitPosition()) diff --git a/server/src/com/vaadin/ui/AbstractTextField.java b/server/src/com/vaadin/ui/AbstractTextField.java index 8bca74936a..adeb1cb69f 100644 --- a/server/src/com/vaadin/ui/AbstractTextField.java +++ b/server/src/com/vaadin/ui/AbstractTextField.java @@ -803,8 +803,8 @@ public abstract class AbstractTextField extends AbstractField<String> implements @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); - AbstractTextField def = designContext.getDefaultInstance(this - .getClass()); + AbstractTextField def = (AbstractTextField) designContext + .getDefaultInstance(this); Attributes attr = design.attributes(); DesignAttributeHandler.writeAttribute("maxlength", attr, getMaxLength(), def.getMaxLength(), Integer.class); diff --git a/server/src/com/vaadin/ui/Button.java b/server/src/com/vaadin/ui/Button.java index 9b50b5eebc..7063568f26 100644 --- a/server/src/com/vaadin/ui/Button.java +++ b/server/src/com/vaadin/ui/Button.java @@ -727,7 +727,7 @@ public class Button extends AbstractComponent implements public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); Attributes attr = design.attributes(); - Button def = designContext.getDefaultInstance(this.getClass()); + Button def = (Button) designContext.getDefaultInstance(this); String content = getCaption(); if (content != null) { design.html(content); diff --git a/server/src/com/vaadin/ui/Panel.java b/server/src/com/vaadin/ui/Panel.java index b0242cc32d..0c2a3f580b 100644 --- a/server/src/com/vaadin/ui/Panel.java +++ b/server/src/com/vaadin/ui/Panel.java @@ -365,7 +365,7 @@ public class Panel extends AbstractSingleComponentContainer implements public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); // handle tabindex - Panel def = designContext.getDefaultInstance(this.getClass()); + Panel def = (Panel) designContext.getDefaultInstance(this); DesignAttributeHandler.writeAttribute("tabindex", design.attributes(), getTabIndex(), def.getTabIndex(), Integer.class); } diff --git a/server/src/com/vaadin/ui/PasswordField.java b/server/src/com/vaadin/ui/PasswordField.java index 974a1c2c10..1894804775 100644 --- a/server/src/com/vaadin/ui/PasswordField.java +++ b/server/src/com/vaadin/ui/PasswordField.java @@ -107,8 +107,8 @@ public class PasswordField extends AbstractTextField { @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); - AbstractTextField def = designContext.getDefaultInstance(this - .getClass()); + AbstractTextField def = (AbstractTextField) designContext + .getDefaultInstance(this); Attributes attr = design.attributes(); DesignAttributeHandler.writeAttribute("value", attr, getValue(), def.getValue(), String.class); diff --git a/server/src/com/vaadin/ui/TabSheet.java b/server/src/com/vaadin/ui/TabSheet.java index 330acacfd0..a264692acb 100644 --- a/server/src/com/vaadin/ui/TabSheet.java +++ b/server/src/com/vaadin/ui/TabSheet.java @@ -1623,7 +1623,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); - TabSheet def = designContext.getDefaultInstance(this.getClass()); + TabSheet def = (TabSheet) designContext.getDefaultInstance(this); Attributes attr = design.attributes(); // handle tab index DesignAttributeHandler.writeAttribute("tabindex", attr, getTabIndex(), diff --git a/server/src/com/vaadin/ui/TextField.java b/server/src/com/vaadin/ui/TextField.java index cd919ded9c..563810c76a 100644 --- a/server/src/com/vaadin/ui/TextField.java +++ b/server/src/com/vaadin/ui/TextField.java @@ -129,8 +129,8 @@ public class TextField extends AbstractTextField { @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); - AbstractTextField def = designContext.getDefaultInstance(this - .getClass()); + AbstractTextField def = (AbstractTextField) designContext + .getDefaultInstance(this); Attributes attr = design.attributes(); DesignAttributeHandler.writeAttribute("value", attr, getValue(), def.getValue(), String.class); diff --git a/server/src/com/vaadin/ui/declarative/DesignContext.java b/server/src/com/vaadin/ui/declarative/DesignContext.java index b196c84b02..214040b54d 100644 --- a/server/src/com/vaadin/ui/declarative/DesignContext.java +++ b/server/src/com/vaadin/ui/declarative/DesignContext.java @@ -28,7 +28,10 @@ import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.nodes.Node; +import com.vaadin.annotations.DesignRoot; +import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.Component; +import com.vaadin.ui.HasComponents; /** * This class contains contextual information that is collected when a component @@ -42,8 +45,8 @@ import com.vaadin.ui.Component; public class DesignContext implements Serializable { // cache for object instances - private static Map<Class<?>, Object> instanceCache = Collections - .synchronizedMap(new HashMap<Class<?>, Object>()); + private static Map<Class<?>, Component> instanceCache = Collections + .synchronizedMap(new HashMap<Class<?>, Component>()); // The root component of the component hierarchy private Component rootComponent = null; @@ -254,22 +257,38 @@ public class DesignContext implements Serializable { * Returns the default instance for the given class. The instance must not * be modified by the caller. * - * @param instanceClass + * @param abstractComponent * @return the default instance for the given class. The return value must * not be modified by the caller */ - public <T> T getDefaultInstance(Class<T> instanceClass) { - T instance = (T) instanceCache.get(instanceClass); + public <T> T getDefaultInstance(AbstractComponent abstractComponent) { + // If the root is a @DesignRoot component, it can't use itself as a + // reference or the written design will be empty + + // If the root component in some other way initializes itself in the + // constructor + if (getRootComponent() == abstractComponent + && abstractComponent.getClass().isAnnotationPresent( + DesignRoot.class)) { + return (T) getDefaultInstance((Class<? extends Component>) abstractComponent + .getClass().getSuperclass()); + } + return (T) getDefaultInstance(abstractComponent.getClass()); + } + + private Component getDefaultInstance( + Class<? extends Component> componentClass) { + Component instance = instanceCache.get(componentClass); if (instance == null) { try { - instance = instanceClass.newInstance(); - instanceCache.put(instanceClass, instance); + instance = componentClass.newInstance(); + instanceCache.put(componentClass, instance); } catch (InstantiationException e) { throw new RuntimeException("Could not instantiate " - + instanceClass.getName()); + + componentClass.getName()); } catch (IllegalAccessException e) { throw new RuntimeException("Could not instantiate " - + instanceClass.getName()); + + componentClass.getName()); } } return instance; @@ -663,4 +682,30 @@ public class DesignContext implements Serializable { } } + /** + * Helper method for component write implementors to determine whether their + * children should be written out or not + * + * @param c + * The component being written + * @param defaultC + * The default instance for the component + * @return + */ + public boolean shouldWriteChildren(Component c, Component defaultC) { + if (c == getRootComponent()) { + // The root component should always write its children - otherwise + // the result is empty + return true; + } + + if (defaultC instanceof HasComponents + && ((HasComponents) defaultC).iterator().hasNext()) { + // Easy version which assumes that this is a custom component if the + // constructor adds children + return false; + } + + return true; + } } diff --git a/server/tests/src/com/vaadin/tests/design/nested/MyChildDesign.java b/server/tests/src/com/vaadin/tests/design/nested/MyChildDesign.java new file mode 100644 index 0000000000..e85c0aca5f --- /dev/null +++ b/server/tests/src/com/vaadin/tests/design/nested/MyChildDesign.java @@ -0,0 +1,40 @@ +/* + * 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.design.nested; + +import org.junit.Ignore; + +import com.vaadin.annotations.DesignRoot; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.declarative.Design; + +/** + * Child design component + * + * @author Vaadin Ltd + */ +@Ignore +@DesignRoot("mychilddesign.html") +public class MyChildDesign extends HorizontalLayout { + public Label childLabel; + public MyChildDesignCustomComponent childCustomComponent; + + public MyChildDesign() { + Design.read(this); + childLabel.setDescription("added in constructor"); + } +} diff --git a/server/tests/src/com/vaadin/tests/design/nested/MyChildDesignCustomComponent.java b/server/tests/src/com/vaadin/tests/design/nested/MyChildDesignCustomComponent.java new file mode 100644 index 0000000000..94e34baea2 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/design/nested/MyChildDesignCustomComponent.java @@ -0,0 +1,25 @@ +/* + * 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.design.nested; + +import org.junit.Ignore; + +import com.vaadin.ui.Button; + +@Ignore +public class MyChildDesignCustomComponent extends Button { + +} diff --git a/server/tests/src/com/vaadin/tests/design/nested/MyDesignRoot.java b/server/tests/src/com/vaadin/tests/design/nested/MyDesignRoot.java new file mode 100644 index 0000000000..5727322ce3 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/design/nested/MyDesignRoot.java @@ -0,0 +1,38 @@ +/* + * 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.design.nested; + +import org.junit.Ignore; + +import com.vaadin.annotations.DesignRoot; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.declarative.Design; + +/** + * Root design component + * + * @author Vaadin Ltd + */ +@Ignore +@DesignRoot("mydesignroot.html") +public class MyDesignRoot extends VerticalLayout { + // should be assigned automatically + public MyExtendedChildDesign childDesign; + + public MyDesignRoot() { + Design.read(this); + } +} diff --git a/server/tests/src/com/vaadin/tests/design/nested/MyExtendedChildDesign.java b/server/tests/src/com/vaadin/tests/design/nested/MyExtendedChildDesign.java new file mode 100644 index 0000000000..2012e4ec14 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/design/nested/MyExtendedChildDesign.java @@ -0,0 +1,25 @@ +/* + * 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.design.nested; + +import org.junit.Ignore; + +@Ignore +public class MyExtendedChildDesign extends MyChildDesign { + public MyExtendedChildDesign() { + super(); + } +} diff --git a/server/tests/src/com/vaadin/tests/design/nested/TestReadNestedTemplates.java b/server/tests/src/com/vaadin/tests/design/nested/TestReadNestedTemplates.java new file mode 100644 index 0000000000..c1483adc8a --- /dev/null +++ b/server/tests/src/com/vaadin/tests/design/nested/TestReadNestedTemplates.java @@ -0,0 +1,62 @@ +/* + * 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.design.nested; + +import junit.framework.TestCase; + +/** + * Test case for reading nested templates + * + * @since + * @author Vaadin Ltd + */ +public class TestReadNestedTemplates extends TestCase { + + private MyDesignRoot root; + + @Override + protected void setUp() throws Exception { + super.setUp(); + root = new MyDesignRoot(); + } + + public void testChildren() { + assertEquals("The root layout must contain one child", 1, + root.getComponentCount()); + assertTrue(root.iterator().next() instanceof MyExtendedChildDesign); + } + + public void testGrandChildren() { + assertEquals("The root layout must have two grandchildren", 2, + root.childDesign.getComponentCount()); + } + + public void testRootComponentFields() { + assertNotNull("The child component must not be null", root.childDesign); + } + + public void testChildComponentFields() { + assertNotNull("Grandchildren must not be null", + root.childDesign.childLabel); + assertNotNull("Grandchildren must not be null", + root.childDesign.childCustomComponent); + assertEquals("child label caption must be read", "test content", + root.childDesign.childLabel.getValue()); + assertEquals("child custom component caption must be read", + "custom content", + root.childDesign.childCustomComponent.getCaption()); + } +} diff --git a/server/tests/src/com/vaadin/tests/design/nested/TestWriteNestedTemplates.java b/server/tests/src/com/vaadin/tests/design/nested/TestWriteNestedTemplates.java new file mode 100644 index 0000000000..7fe63baa73 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/design/nested/TestWriteNestedTemplates.java @@ -0,0 +1,89 @@ +/* + * 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.design.nested; + +import junit.framework.TestCase; + +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; +import org.jsoup.parser.Tag; + +import com.vaadin.ui.declarative.DesignContext; + +/** + * + * Test case for writing nested templates + * + * @author Vaadin Ltd + */ +public class TestWriteNestedTemplates extends TestCase { + + private MyDesignRoot root; + private Element design; + + @Override + protected void setUp() throws Exception { + super.setUp(); + root = new MyDesignRoot(); + design = createDesign(); + DesignContext designContext = new DesignContext(); + designContext.setRootComponent(root); + root.writeDesign(design, designContext); + } + + public void testChildRendered() { + assertEquals("Root layout must have one child", 1, design.children() + .size()); + assertEquals("com_vaadin_tests_design_nested-my-extended-child-design", + design.child(0).tagName()); + } + + public void testRootCaptionWritten() { + assertTrue("Root layout caption must be written", + design.hasAttr("caption")); + assertEquals("Root layout caption must be written", "root caption", + design.attr("caption")); + } + + public void testChildCaptionWritten() { + assertTrue("Child design caption must be written", design.child(0) + .hasAttr("caption")); + assertEquals("Child design caption must be written", "child caption", + design.child(0).attr("caption")); + } + + // The default caption is read from child template + public void testDefaultCaptionShouldNotBeWritten() { + design = createDesign(); + root.childDesign.setCaption("Default caption for child design"); + DesignContext designContext = new DesignContext(); + designContext.setRootComponent(root); + root.writeDesign(design, designContext); + assertFalse("Default caption must not be written", design.child(0) + .hasAttr("caption")); + } + + public void testChildDesignChildrenNotWrittenInRootTemplate() { + assertEquals( + "Children of the child template must not be written to root template", + 0, design.child(0).children().size()); + } + + private Element createDesign() { + return new Element(Tag.valueOf("v-vertical-layout"), "", + new Attributes()); + } +} diff --git a/server/tests/src/com/vaadin/tests/design/nested/mychilddesign.html b/server/tests/src/com/vaadin/tests/design/nested/mychilddesign.html new file mode 100644 index 0000000000..c636033d2d --- /dev/null +++ b/server/tests/src/com/vaadin/tests/design/nested/mychilddesign.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html> + <head> + <meta name="package-mapping" content="x:com.vaadin.tests.design.nested"/> + </head> + <body> + <v-horizontal-layout caption="Default caption for child design"> + <v-label _id="childLabel">test content</v-label> + <!-- Test some custom component in child template --> + <x-my-child-design-custom-component _id="childCustomComponent">custom content</x-my-child-design-custom-component> + </v-horizontal-layout> +</body>
\ No newline at end of file diff --git a/server/tests/src/com/vaadin/tests/design/nested/mydesignroot.html b/server/tests/src/com/vaadin/tests/design/nested/mydesignroot.html new file mode 100644 index 0000000000..55ac27c194 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/design/nested/mydesignroot.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> + <head> + <meta name="package-mapping" content="x:com.vaadin.tests.design.nested"/> + </head> + <body> + <v-vertical-layout caption="root caption"> + <x-my-extended-child-design _id="childDesign" caption="child caption"/> + </v-vertical-layout> + </body>
\ No newline at end of file |