Browse Source

Declarative support for AbstractOrderedLayout

tags/7.4.0.beta1
Matti Hosio 9 years ago
parent
commit
d3af8be5a5

+ 115
- 0
server/src/com/vaadin/ui/AbstractOrderedLayout.java View File

@@ -18,6 +18,11 @@ package com.vaadin.ui;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.logging.Logger;

import org.jsoup.nodes.Attributes;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;

import com.vaadin.event.LayoutEvents.LayoutClickEvent;
import com.vaadin.event.LayoutEvents.LayoutClickListener;
@@ -26,10 +31,13 @@ import com.vaadin.server.Sizeable;
import com.vaadin.shared.Connector;
import com.vaadin.shared.EventId;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.ui.AlignmentInfo;
import com.vaadin.shared.ui.MarginInfo;
import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutServerRpc;
import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutState;
import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutState.ChildComponentData;
import com.vaadin.ui.declarative.DesignAttributeHandler;
import com.vaadin.ui.declarative.DesignContext;

@SuppressWarnings("serial")
public abstract class AbstractOrderedLayout extends AbstractLayout implements
@@ -459,4 +467,111 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements
setExpandRatio(target, expandRatio);
}

/*
* (non-Javadoc)
*
* @see
* com.vaadin.ui.AbstractComponent#synchronizeFromDesign(org.jsoup.nodes
* .Node, com.vaadin.ui.declarative.DesignContext)
*/
@Override
public void synchronizeFromDesign(Node design, DesignContext designContext) {
// process default attributes
super.synchronizeFromDesign(design, designContext);
// remove current children
removeAllComponents();
// handle children
for (Node childComponent : design.childNodes()) {
if (childComponent instanceof Element) {
Attributes attr = childComponent.attributes();
DesignSynchronizable newChild = designContext
.createChild(childComponent);
newChild.synchronizeFromDesign(childComponent, designContext);
addComponent(newChild);
// handle alignment
int bitMask = 0;
if (attr.hasKey(":middle")) {
bitMask += AlignmentInfo.Bits.ALIGNMENT_VERTICAL_CENTER;
} else if (attr.hasKey(":bottom")) {
bitMask += AlignmentInfo.Bits.ALIGNMENT_BOTTOM;
} else {
bitMask += AlignmentInfo.Bits.ALIGNMENT_TOP;
}
if (attr.hasKey(":center")) {
bitMask += AlignmentInfo.Bits.ALIGNMENT_HORIZONTAL_CENTER;
} else if (attr.hasKey(":right")) {
bitMask += AlignmentInfo.Bits.ALIGNMENT_RIGHT;
} else {
bitMask += AlignmentInfo.Bits.ALIGNMENT_LEFT;
}
setComponentAlignment(newChild, new Alignment(bitMask));
// handle expand ratio
if (attr.hasKey(":expand")) {
String value = attr.get(":expand");
if (value.length() > 0) {
try {
float ratio = Float.valueOf(value);
setExpandRatio(newChild, ratio);
} catch (NumberFormatException nfe) {
getLogger().info(
"Failed to parse expand ratio " + value);
}
} else {
setExpandRatio(newChild, 1.0f);
}
}
}
}
}

/*
* (non-Javadoc)
*
* @see
* com.vaadin.ui.AbstractComponent#synchronizeToDesign(org.jsoup.nodes.Node,
* com.vaadin.ui.declarative.DesignContext)
*/
@Override
public void synchronizeToDesign(Node design, DesignContext designContext) {
// synchronize default attributes
super.synchronizeToDesign(design, designContext);
// handle children
if (this instanceof HasComponents) {
if (!(design instanceof Element)) {
throw new RuntimeException(
"Attempted to create child elements for a node that cannot have children.");
}
Element designElement = (Element) design;
for (Component child : this) {
DesignSynchronizable childComponent = (DesignSynchronizable) child;
Node childNode = designContext.createNode(childComponent);
designElement.appendChild(childNode);
childComponent.synchronizeToDesign(childNode, designContext);
// handle alignment
Alignment alignment = getComponentAlignment(child);
if (alignment.isMiddle()) {
childNode.attr(":middle", "");
} else if (alignment.isBottom()) {
childNode.attr(":bottom", "");
}
if (alignment.isCenter()) {
childNode.attr(":center", "");
} else if (alignment.isRight()) {
childNode.attr(":right", "");
}
// handle expand ratio
float expandRatio = getExpandRatio(child);
if (expandRatio == 1.0f) {
childNode.attr(":expand", "");
} else if (expandRatio > 0) {
childNode.attr(":expand", DesignAttributeHandler
.formatDesignAttribute(expandRatio));
}
}
}
}

private static Logger getLogger() {
return Logger.getLogger(AbstractOrderedLayout.class.getName());
}
}

+ 20
- 6
server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java View File

@@ -297,9 +297,9 @@ public class DesignAttributeHandler {
} else if (widthAuto) {
attributes.put("width-auto", "true");
} else {
DecimalFormat fmt = new DecimalFormat();
attributes.put("width", fmt.format(component.getWidth())
+ component.getWidthUnits().getSymbol());
attributes.put("width",
formatDesignAttribute(component.getWidth())
+ component.getWidthUnits().getSymbol());
}
}
if (!areEqualHeight(component, defaultInstance)) {
@@ -309,14 +309,28 @@ public class DesignAttributeHandler {
} else if (heightAuto) {
attributes.put("height-auto", "true");
} else {
DecimalFormat fmt = new DecimalFormat();
attributes.put("height", fmt.format(component.getHeight())
+ component.getHeightUnits().getSymbol());
attributes.put("height",
formatDesignAttribute(component.getHeight())
+ component.getHeightUnits().getSymbol());
}
}
}
}

/**
* Formats the given design attribute value. The method is provided to
* ensure consistent number formatting for design attribute values
*
* @since 7.4
* @param number
* the number to be formatted
* @return the formatted number
*/
public static String formatDesignAttribute(float number) {
DecimalFormat fmt = new DecimalFormat();
return fmt.format(number);
}

/**
* Returns the design attribute name corresponding the given method name.
* For example given a method name <code>setPrimaryStyleName</code> the

+ 365
- 3
server/src/com/vaadin/ui/declarative/DesignContext.java View File

@@ -17,13 +17,27 @@ package com.vaadin.ui.declarative;

import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.Attributes;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;

import com.vaadin.ui.Component;
import com.vaadin.ui.DesignSynchronizable;

/**
* This class contains contextual information that is collected when a component
* tree is constructed based on HTML design template
* DesignContext can create a component corresponding to a given html tree node
* or an html tree node corresponding to a given component. DesignContext also
* keeps track of id values found in the current html tree and can detect
* non-uniqueness of these values. Non-id attributes are handled by the
* component classes instead of DesignContext.
*
* @since 7.4
* @since
* @author Vaadin Ltd
*/
public class DesignContext {
@@ -32,6 +46,353 @@ public class DesignContext {
private static Map<Class<?>, Object> instanceCache = Collections
.synchronizedMap(new HashMap<Class<?>, Object>());

public static final String ID_ATTRIBUTE = "id";
public static final String CAPTION_ATTRIBUTE = "caption";
public static final String LOCAL_ID_PREFIX = "_";
private Map<String, DesignSynchronizable> globalIds = new HashMap<String, DesignSynchronizable>();
private Map<String, DesignSynchronizable> localIds = new HashMap<String, DesignSynchronizable>();
private Map<String, DesignSynchronizable> captions = new HashMap<String, DesignSynchronizable>();
private Document doc; // used for accessing
// Document.createElement(String)
// namespace mappings
private Map<String, String> packageToPrefix = new HashMap<String, String>();
private Map<String, String> prefixToPackage = new HashMap<String, String>();
// prefix names for which no package-mapping element will be created in the
// html tree
private Map<String, String> defaultPrefixes = new HashMap<String, String>();

public DesignContext() {
doc = new Document("");
// Initialize the mapping between prefixes and package names. First add
// any default mappings (v -> com.vaadin.ui). The default mappings are
// the prefixes for which
// no meta tags will be created when writing a design to html.
defaultPrefixes.put("v", "com.vaadin.ui");
for (String prefix : defaultPrefixes.keySet()) {
String packageName = defaultPrefixes.get(prefix);
prefixToPackage.put(prefix, packageName);
packageToPrefix.put(packageName, prefix);
}
}

/**
* Get the mappings from prefixes to package names from meta tags located
* under <head> in the html document.
*
* @since
*/
public void getPrefixes(Document doc) {
// TODO this method has not been tested in any way.
Element head = doc.head();
if (head == null) {
return;
}
for (Node child : head.childNodes()) {
if (child instanceof Element) {
Element childElement = (Element) child;
if ("meta".equals(childElement.tagName())) {
Attributes attributes = childElement.attributes();
if (attributes.hasKey("name")
&& attributes.hasKey("content")
&& "package-mapping".equals(attributes.get("name"))) {
String contentString = attributes.get("content");
String[] parts = contentString.split(":");
if (parts.length != 2) {
throw new RuntimeException("The meta tag '"
+ child.toString() + "' cannot be parsed.");
}
String prefixName = parts[0];
String packageName = parts[1];
prefixToPackage.put(prefixName, packageName);
packageToPrefix.put(packageName, prefixName);
}
}
}
}
}

/**
* Creates an html tree node corresponding to the given element. Note that
* this method does not set the attribute values. That can be done by
* calling childComponent.synchronizeToDesign(result, designContext), where
* result is the node returned by this method and designContext is this
* context.
*
* @since
* @param childComponent
* A component implementing the DesignSynchronizable interface.
* @return An html tree node corresponding to the given component, with no
* attributes set. The tag name of the created node is derived from
* the class name of childComponent.
*/
public Node createNode(DesignSynchronizable childComponent) {
// TODO handle namespaces and id's.
Class<?> componentClass = childComponent.getClass();
String packageName = componentClass.getPackage().getName();
String prefix = packageToPrefix.get(packageName);
if (prefix == null) {
prefix = packageName.replace('.', '_');
prefixToPackage.put(prefix, packageName);
packageToPrefix.put(packageName, prefix);
}
prefix = prefix + "-";
String className = classNameToElementName(componentClass
.getSimpleName());
Element newElement = doc.createElement(prefix + className);
return newElement;
}

/**
* Creates the name of the html tag corresponding to the given class name.
* The name is derived by converting each uppercase letter to lowercase and
* inserting a dash before the letter. No dash is inserted before the first
* letter of the class name.
*
* @since
* @param className
* the name of the class without a package name
* @return the html tag name corresponding to className
*/
private String classNameToElementName(String className) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < className.length(); i++) {
Character c = className.charAt(i);
if (Character.isUpperCase(c)) {
if (i > 0) {
result.append("-");
}
result.append(Character.toLowerCase(c));
} else {
result.append(c);
}
}
return result.toString();
}

/**
* Creates a DesignSynchronizable object corresponding to the given html
* node. Note that the attributes of the node are not taken into account by
* this method, except IDs. To get the attributes, call
* result.synchronizeFromDesign(componentDesign, designContext), where
* result is the node returned by this method and designContext is this
* context.
*
* @since
* @param componentDesign
* The html tree node containing the description of the component
* to be created.
* @return a DesignSynchronizable object corresponding to componentDesign,
* with no attributes set.
*/
public DesignSynchronizable createChild(Node componentDesign) {
// Create the component.
DesignSynchronizable component = instantiateComponent(componentDesign);
// Get the IDs and the caption of the component and store them in the
// maps of this design context.
org.jsoup.nodes.Attributes attributes = componentDesign.attributes();

// Global id
String id = attributes.get(ID_ATTRIBUTE);
if (id != null && id.length() > 0) {
Component oldComponent = globalIds.put(camelCase(id), component);
if (oldComponent != null) {
throw new RuntimeException("Duplicate ids: " + id);
}
}

// Local id
String localId = null;
for (Attribute attribute : attributes.asList()) {
if (attribute.getKey().startsWith(LOCAL_ID_PREFIX)) {
if (localId != null) {
throw new RuntimeException(
"Duplicate local ids specified: " + localId
+ " and " + attribute.getValue());
}
localId = attribute.getKey()
.substring(LOCAL_ID_PREFIX.length());
localIds.put(camelCase(localId), component);
}
}

// Caption
String caption = null;
if (componentDesign.nodeName().equals("v-button")) {
String buttonCaption = textContent(componentDesign);
if (buttonCaption != null && !(buttonCaption.equals(""))) {
caption = buttonCaption;
}
}
if (caption == null) {
String componentCaption = attributes.get(CAPTION_ATTRIBUTE);
if (componentCaption != null && !("".equals(componentCaption))) {
caption = componentCaption;
}
}
if (caption != null) {
Component oldComponent = captions
.put(camelCase(caption), component);
if (oldComponent != null) {
throw new RuntimeException("Duplicate captions: " + caption);
}
}
return component;
}

/**
* Returns the text content of an html tree node. Used for getting the
* caption of a button.
*
* @since
* @param node
* A node of an html tree
* @return the text content of node, obtained by concatenating the text
* contents of its children
*/
private String textContent(Node node) {
String text = "";
for (Node child : node.childNodes()) {
if (child instanceof TextNode) {
text += ((TextNode) child).text();
}
}
return text;
}

/**
* Creates a DesignSynchronizable component corresponding to the given node.
*
* @since
* @param node
* a node of an html tree
* @return a DesignSynchronizable object corresponding to node
*/
private DesignSynchronizable instantiateComponent(Node node) {
// Extract the package and class names.
String qualifiedClassName = tagNameToClassName(node);
return createComponent(qualifiedClassName);
}

private String tagNameToClassName(Node node) {
String tagName = node.nodeName();
if (tagName.equals("v-addon")) {
return node.attr("class");
} else if (tagName.toLowerCase(Locale.ENGLISH).equals("span")
|| tagName.toLowerCase(Locale.ENGLISH).equals("div")) {
return "com.vaadin.ui.Label";
}
// Otherwise, get the package name from the prefixToPackage mapping.
String[] parts = tagName.split("-");
if (parts.length < 2) {
throw new RuntimeException("The tagname '" + tagName
+ "' is invalid: missing prefix.");
}
String prefixName = parts[0];
String packageName = prefixToPackage.get(prefixName);
if (packageName == null) {
throw new RuntimeException("Unknown tag: " + tagName);
}
// v-vertical-layout -> com.vaadin.ui.VerticalLayout
int firstCharacterIndex = prefixName.length() + 1; // +1 is for '-'
tagName = tagName.substring(firstCharacterIndex,
firstCharacterIndex + 1).toUpperCase(Locale.ENGLISH)
+ tagName.substring(firstCharacterIndex + 1);
int i;
while ((i = tagName.indexOf("-")) != -1) {
int length = tagName.length();
if (i != length - 1) {
tagName = tagName.substring(0, i)
+ tagName.substring(i + 1, i + 2).toUpperCase(
Locale.ENGLISH) + tagName.substring(i + 2);

} else {
// Ends with "-", WTF?
System.out.println("ends with '-', really?");
}
}
return packageName + "." + tagName;
}

/**
* Returns a new component instance of given class name. If the component
* cannot be instantiated a ComponentInstantiationException is thrown.
*
* @param qualifiedClassName
* The full class name of the object to be created.
* @return a new DesignSynchronizable instance.
* @throws ComponentInstantiationException
*/
public DesignSynchronizable createComponent(String qualifiedClassName) {
try {
Class<? extends DesignSynchronizable> componentClass = resolveComponentClass(qualifiedClassName);
DesignSynchronizable newComponent = componentClass.newInstance();
return newComponent;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

@SuppressWarnings("unchecked")
private Class<? extends DesignSynchronizable> resolveComponentClass(
String qualifiedClassName) throws ClassNotFoundException {
Class<?> componentClass = null;
componentClass = Class.forName(qualifiedClassName);

// Check that we're dealing with a DesignSynchronizable component.
if (isDesignSynchronizable(componentClass)) {
return (Class<? extends DesignSynchronizable>) componentClass;
} else {
throw new IllegalArgumentException(String.format(
"Resolved class %s is not a %s.", componentClass.getName(),
Component.class.getName()));
}
}

/**
* Returns {@code true} if the given {@link Class} implements the
* {@link Component} interface of Vaadin Framework otherwise {@code false}.
*
* @param componentClass
* {@link Class} to check against {@link Component} interface.
* @return {@code true} if the given {@link Class} is a {@link Component},
* {@code false} otherwise.
*/
public static boolean isDesignSynchronizable(Class<?> componentClass) {
if (componentClass != null) {
return DesignSynchronizable.class.isAssignableFrom(componentClass);
} else {
return false;
}
}

private String camelCase(String localId) {
// TODO does this method do what it should (it was taken from another
// project without any modifications)

// Remove all but a-Z, 0-9 (used for names) and _- and space (used
// for separators)
// localId = localId.replaceAll("[^a-zA-Z0-9_- ]", "");
return localId.replaceAll("[^a-zA-Z0-9]", "").toLowerCase(
Locale.ENGLISH);
// String[] parts = localId.split("[ -_]+");
// String thisPart = parts[0];
// String camelCase =
// thisPart.substring(0,1).toLowerCase(Locale.ENGLISH);
// if (parts[0].length() > 1) {
// camelCase += thisPart.substring(1);
// }
//
// for (int i=1; i < parts.length; i++) {
// thisPart = parts[i];
// camelCase += thisPart.substring(0,1).toUpperCase(Locale.ENGLISH);
// if (thisPart.length() > 1) {
// camelCase += thisPart.substring(1);
// }
// }
// return camelCase;
}

/**
* Returns the default instance for the given class. The instance must not
* be modified by the caller.
@@ -54,4 +415,5 @@ public class DesignContext {
}
return instance;
}

}

+ 109
- 0
server/tests/src/com/vaadin/tests/server/component/abstractorderedlayout/TestSynchronizeFromDesign.java View File

@@ -0,0 +1,109 @@
/*
* 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.abstractorderedlayout;

import junit.framework.TestCase;

import org.jsoup.nodes.Attributes;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.parser.Tag;

import com.vaadin.ui.Alignment;
import com.vaadin.ui.DesignSynchronizable;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.declarative.DesignContext;

/**
* Test case from reading AbstractOrdered layouts from design
*
* @since
* @author Vaadin Ltd
*/
public class TestSynchronizeFromDesign extends TestCase {

public void testChildCount() {
VerticalLayout root = createLayout(0f);
assertEquals(2, root.getComponentCount());
}

public void testAttributes() {
VerticalLayout root = createLayout(0f);
assertEquals("test-layout", root.getCaption());
assertEquals("test-label", root.getComponent(0).getCaption());
assertEquals("test-button", root.getComponent(1).getCaption());
}

public void testExpandRatio() {
VerticalLayout root = createLayout(1f);
assertEquals(1f, root.getExpandRatio(root.getComponent(0)));
assertEquals(1f, root.getExpandRatio(root.getComponent(1)));

root = createLayout(0f);
assertEquals(0f, root.getExpandRatio(root.getComponent(0)));
assertEquals(0f, root.getExpandRatio(root.getComponent(1)));
}

public void testAlignment() {
VerticalLayout root = createLayout(0f, ":top", ":left");
assertEquals(Alignment.TOP_LEFT,
root.getComponentAlignment(root.getComponent(0)));
root = createLayout(0f, ":middle", ":center");
assertEquals(Alignment.MIDDLE_CENTER,
root.getComponentAlignment(root.getComponent(0)));
root = createLayout(0f, ":bottom", ":right");
assertEquals(Alignment.BOTTOM_RIGHT,
root.getComponentAlignment(root.getComponent(0)));

}

private VerticalLayout createLayout(float expandRatio, String... alignments) {
DesignContext ctx = new DesignContext();
Node design = createDesign(expandRatio, alignments);
DesignSynchronizable child = ctx.createChild(design);
child.synchronizeFromDesign(design, ctx);
return (VerticalLayout) child;
}

private Node createDesign(float expandRatio, String... alignments) {

Attributes rootAttributes = new Attributes();
rootAttributes.put("caption", "test-layout");
Element node = new Element(Tag.valueOf("v-vertical-layout"), "",
rootAttributes);

Attributes firstChildAttributes = new Attributes();
firstChildAttributes.put("caption", "test-label");
firstChildAttributes.put(":expand", String.valueOf(expandRatio));
for (String alignment : alignments) {
firstChildAttributes.put(alignment, "");
}
Element firstChild = new Element(Tag.valueOf("v-label"), "",
firstChildAttributes);
node.appendChild(firstChild);

Attributes secondChildAttributes = new Attributes();
secondChildAttributes.put("caption", "test-button");
secondChildAttributes.put(":expand", String.valueOf(expandRatio));
for (String alignment : alignments) {
secondChildAttributes.put(alignment, "");
}
Element secondChild = new Element(Tag.valueOf("v-button"), "",
secondChildAttributes);
node.appendChild(secondChild);
return node;
}
}

+ 139
- 0
server/tests/src/com/vaadin/tests/server/component/abstractorderedlayout/TestSynchronizeToDesign.java View File

@@ -0,0 +1,139 @@
/*
* 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.abstractorderedlayout;

import junit.framework.TestCase;

import org.jsoup.nodes.Attributes;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.parser.Tag;

import com.vaadin.ui.Alignment;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.declarative.DesignContext;

/**
* Test case for writing abstract ordered layout to design
*
* @since
* @author Vaadin Ltd
*/
public class TestSynchronizeToDesign extends TestCase {

public void testSynchronizeEmptyLayout() {
VerticalLayout layout = new VerticalLayout();
layout.setCaption("changed-caption");
Node design = createDesign();
layout.synchronizeToDesign(design, createDesignContext());
assertEquals(0, design.childNodes().size());
assertEquals("changed-caption", design.attr("caption"));
}

public void testSynchronizeLayoutWithChildren() {
VerticalLayout layout = new VerticalLayout();
layout.addComponent(new Label("test-label"));
layout.getComponent(0).setCaption("test-caption");
layout.addComponent(new Label("test-label-2"));
Node design = createDesign();
layout.synchronizeToDesign(design, createDesignContext());
assertEquals(2, design.childNodes().size());
assertEquals("v-label", ((Element) design.childNode(0)).tagName());
assertEquals("test-caption", design.childNode(0).attr("caption"));
}

public void testSynchronizeUnitExpandRatio() {
VerticalLayout layout = new VerticalLayout();
layout.addComponent(new Label("test-label"));
layout.setExpandRatio(layout.getComponent(0), 1.0f);
Node design = createDesign();
layout.synchronizeToDesign(design, createDesignContext());
assertTrue(design.childNode(0).hasAttr(":expand"));
assertEquals("", design.childNode(0).attr(":expand"));
}

public void testSynchronizeArbitraryExpandRatio() {
VerticalLayout layout = new VerticalLayout();
layout.addComponent(new Label("test-label"));
layout.setExpandRatio(layout.getComponent(0), 2.40f);
Node design = createDesign();
layout.synchronizeToDesign(design, createDesignContext());
assertTrue(design.childNode(0).hasAttr(":expand"));
assertEquals("2.4", design.childNode(0).attr(":expand"));
}

public void testSynchronizeDefaultAlignment() {
Node design = createDesign();
VerticalLayout layout = createLayoutWithAlignment(design, null);
layout.synchronizeToDesign(design, createDesignContext());
assertFalse(design.childNode(0).hasAttr(":top"));
assertFalse(design.childNode(0).hasAttr(":left"));
}

public void testSynchronizeMiddleCenter() {
Node design = createDesign();
VerticalLayout layout = createLayoutWithAlignment(design,
Alignment.MIDDLE_CENTER);
layout.synchronizeToDesign(design, createDesignContext());
assertTrue(design.childNode(0).hasAttr(":middle"));
assertTrue(design.childNode(0).hasAttr(":center"));
}

public void testSynchronizeBottomRight() {
Node design = createDesign();
VerticalLayout layout = createLayoutWithAlignment(design,
Alignment.BOTTOM_RIGHT);
layout.synchronizeToDesign(design, createDesignContext());
assertTrue(design.childNode(0).hasAttr(":bottom"));
assertTrue(design.childNode(0).hasAttr(":right"));
}

private VerticalLayout createLayoutWithAlignment(Node design,
Alignment alignment) {
VerticalLayout layout = new VerticalLayout();
layout.addComponent(new Label("test-label"));
if (alignment != null) {
layout.setComponentAlignment(layout.getComponent(0), alignment);
}
layout.synchronizeToDesign(design, createDesignContext());
return layout;
}

private Node createDesign() {
// make sure that the design node has old content that should be removed
Attributes rootAttributes = new Attributes();
rootAttributes.put("caption", "test-layout");
Element node = new Element(Tag.valueOf("v-vertical-layout"), "",
rootAttributes);
Attributes firstChildAttributes = new Attributes();
firstChildAttributes.put("caption", "test-label");
Element firstChild = new Element(Tag.valueOf("v-label"), "",
firstChildAttributes);
node.appendChild(firstChild);

Attributes secondChildAttributes = new Attributes();
secondChildAttributes.put("caption", "test-button");
Element secondChild = new Element(Tag.valueOf("v-button"), "",
secondChildAttributes);
node.appendChild(secondChild);
return node;
}

private DesignContext createDesignContext() {
return new DesignContext();
}
}

Loading…
Cancel
Save