diff options
author | Matti Hosio <mhosio@vaadin.com> | 2014-11-28 16:03:11 +0200 |
---|---|---|
committer | Matti Hosio <mhosio@vaadin.com> | 2014-11-28 16:03:11 +0200 |
commit | d54337ec3259fce95142227935e4471f3712ac68 (patch) | |
tree | b5bb0a965e0a04c4dbcd1ebc65b49cabe0dcc81e | |
parent | 0545cb2999c83b11da55477211b87865bae98bba (diff) | |
download | vaadin-framework-d54337ec3259fce95142227935e4471f3712ac68.tar.gz vaadin-framework-d54337ec3259fce95142227935e4471f3712ac68.zip |
support for declarative in AbstractComponent
5 files changed, 745 insertions, 1 deletions
diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java index 5c4fba739d..78004d0eca 100644 --- a/server/src/com/vaadin/ui/AbstractComponent.java +++ b/server/src/com/vaadin/ui/AbstractComponent.java @@ -26,6 +26,9 @@ import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Node; + import com.vaadin.event.ActionManager; import com.vaadin.event.ConnectorActionManager; import com.vaadin.event.ShortcutListener; @@ -39,6 +42,8 @@ import com.vaadin.shared.ComponentConstants; import com.vaadin.shared.ui.ComponentStateUtil; import com.vaadin.shared.util.SharedUtil; import com.vaadin.ui.Field.ValueChangeEvent; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; import com.vaadin.util.ReflectTools; /** @@ -52,7 +57,7 @@ import com.vaadin.util.ReflectTools; */ @SuppressWarnings("serial") public abstract class AbstractComponent extends AbstractClientConnector - implements Component { + implements Component, DesignSynchronizable { /* Private members */ @@ -897,6 +902,65 @@ public abstract class AbstractComponent extends AbstractClientConnector } /* + * (non-Javadoc) + * + * @see + * com.vaadin.ui.DesignSynchronizable#synchronizeFromDesign(org.jsoup.nodes + * .Node, com.vaadin.ui.declarative.DesignContext) + */ + @Override + public void synchronizeFromDesign(Node design, DesignContext designContext) { + Attributes attr = design.attributes(); + DesignSynchronizable def = designContext.getDefaultInstance(this + .getClass()); + // handle default attributes + for (String property : getDefaultAttributes()) { + String value = null; + if (attr.hasKey(property)) { + value = attr.get(property); + } + DesignAttributeHandler.assignAttribute(this, property, value, def); + } + // handle width and height + DesignAttributeHandler.assignWidth(this, attr, def); + DesignAttributeHandler.assignHeight(this, attr, def); + } + + /** + * Returns the list of attributes that do not require custom handling when + * synchronizing from design. These are typically attributes of some + * primitive type. The default implementation searches setters with + * primitive values + * + * @since 7.4 + * @return the list of attributes that can be synchronized from design using + * the default approach. + */ + protected List<String> getDefaultAttributes() { + List<String> attributes = DesignAttributeHandler + .findSupportedAttributes(this.getClass()); + // we want to handle width and height in a custom way + attributes.remove("width"); + attributes.remove("height"); + attributes.remove("debug-id"); + return attributes; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.ui.DesignSynchronizable#synchronizeToDesign(org.jsoup.nodes + * .Node, com.vaadin.ui.declarative.DesignContext) + */ + @Override + public void synchronizeToDesign(Node design, DesignContext designContext) { + AbstractComponent def = designContext.getDefaultInstance(this + .getClass()); + + } + + /* * Returns array with size in index 0 unit in index 1. Null or empty string * will produce {-1,Unit#PIXELS} */ diff --git a/server/src/com/vaadin/ui/DesignSynchronizable.java b/server/src/com/vaadin/ui/DesignSynchronizable.java new file mode 100644 index 0000000000..a27ba0f539 --- /dev/null +++ b/server/src/com/vaadin/ui/DesignSynchronizable.java @@ -0,0 +1,63 @@ +/* + * 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; + +import org.jsoup.nodes.Node; + +import com.vaadin.ui.declarative.DesignContext; + +/** + * Interface to be implemented by all the components that can be read from or + * written to HTML design representation. TODO: add reference to VisualDesigner + * + * @since 7.4 + * + * @author Vaadin Ltd + */ +public interface DesignSynchronizable extends Component { + + /** + * Update the component state based on the given design. The component is + * responsible not only for updating itself but also ensuring that its + * children update their state based on the design. + * <p> + * This method must not modify the design. + * + * @since 7.4 + * @param design + * The design as HTML to obtain the state from + * @param designContext + * The DesignContext instance used for parsing the design + */ + public void synchronizeFromDesign(Node design, DesignContext designContext); + + /** + * Update the given design based on the component state. The component is + * responsible not only for updating itself but also for ensuring its + * children update themselves in the correct position in the design. The + * caller of this method should not assume that contents of the + * <code>design</code> parameter are presented. + * <p> + * This method must not modify the component state. + * + * @since 7.4 + * @param design + * The design as HTML to update with the current state + * @param designContext + */ + public void synchronizeToDesign(Node design, DesignContext designContext); + +} diff --git a/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java b/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java new file mode 100644 index 0000000000..3b58e0cbc9 --- /dev/null +++ b/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java @@ -0,0 +1,373 @@ +/* + * 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; + +import java.io.File; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jsoup.nodes.Attributes; + +import com.vaadin.server.ExternalResource; +import com.vaadin.server.FileResource; +import com.vaadin.server.FontAwesome; +import com.vaadin.server.Resource; +import com.vaadin.server.ThemeResource; +import com.vaadin.ui.DesignSynchronizable; + +/** + * Default attribute handler implementation used when parsing designs to + * component trees. Handles all the component attributes that do require custom + * handling. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class DesignAttributeHandler { + + protected static Logger getLogger() { + return Logger.getLogger(DesignAttributeHandler.class.getName()); + } + + /** + * Returns the design attribute name corresponding the given method name. + * For example given a method name <code>setPrimaryStyleName</code> the + * return value would be <code>primary-style-name</code> + * + * @since 7.4 + * @param methodName + * the method name + * @return the design attribute name corresponding the given method name + */ + private static String toAttributeName(String methodName) { + String[] words = methodName.split("(?<!^)(?=[A-Z])"); + StringBuilder builder = new StringBuilder(); + // ignore first toget ("set") + for (int i = 1; i < words.length; i++) { + if (builder.length() > 0) { + builder.append("-"); + } + builder.append(words[i].toLowerCase()); + } + return builder.toString(); + } + + /** + * Returns the setter method name corresponding the given design attribute + * name. For example given a attribute name <code>primary-style-name</code> + * the return value would be <code>setPrimaryStyleName</code>. + * + * @since 7.4 + * @param designAttributeName + * the design attribute name + * @return the setter method name corresponding the given design attribute + * name + */ + private static String toSetterName(String designAttributeName) { + String[] parts = designAttributeName.split("-"); + StringBuilder builder = new StringBuilder(); + builder.append("set"); + for (String part : parts) { + builder.append(part.substring(0, 1).toUpperCase()); + builder.append(part.substring(1)); + } + return builder.toString(); + } + + /** + * Parses the given attribute value to specified target type + * + * @since 7.4 + * @param targetType + * the target type for the value + * @param value + * the parsed value + * @return the object of specified target type + */ + private static Object parseAttributeValue(Class<?> targetType, String value) { + if (targetType == String.class) { + return value; + } + // special handling for boolean type. The attribute evaluates to true if + // it is present and the value is not "false" or "FALSE". Thus empty + // value evaluates to true. + if (targetType == Boolean.TYPE || targetType == Boolean.class) { + return value == null || !value.equalsIgnoreCase("false"); + } + if (targetType == Integer.TYPE || targetType == Integer.class) { + return Integer.valueOf(value); + } + if (targetType == Byte.TYPE || targetType == Byte.class) { + return Byte.valueOf(value); + } + if (targetType == Short.TYPE || targetType == Short.class) { + return Short.valueOf(value); + } + if (targetType == Long.TYPE || targetType == Long.class) { + return Long.valueOf(value); + } + if (targetType == Character.TYPE || targetType == Character.class) { + return value.charAt(0); + } + if (targetType == Float.TYPE || targetType == Float.class) { + return Float.valueOf(value); + } + if (targetType == Double.TYPE || targetType == Double.class) { + return Double.valueOf(value); + } + if (targetType == Locale.class) { + return new Locale(value); + } + if (targetType == Resource.class) { + return parseResource(value); + } + return null; + } + + private static Resource parseResource(String value) { + if (value.startsWith("http://")) { + return new ExternalResource("value"); + } else if (value.startsWith("theme://")) { + return new ThemeResource(value.substring(8)); + } else if (value.startsWith("font://")) { + return FontAwesome.valueOf(value.substring(7)); + } else { + return new FileResource(new File(value)); + } + } + + /** + * Finds a corresponding getter method for the given setter method + * + * @since 7.4 + * @param clazz + * the class to search methods from + * @param setter + * the setter that is used to find the matching getter + * @return the matching getter or null if not found + */ + private static Method findGetter(Class<?> clazz, Method setter) { + String propertyName = setter.getName().substring(3); + Class<?> returnType = setter.getParameterTypes()[0]; + for (Method method : clazz.getMethods()) { + if (isGetterForProperty(method, propertyName) + && method.getParameterTypes().length == 0 + && method.getReturnType().equals(returnType)) { + return method; + } + } + getLogger().warning("Could not find getter for " + setter.getName()); + return null; + } + + private static boolean isGetterForProperty(Method method, String property) { + String methodName = method.getName(); + return methodName.equals("get" + property) + || methodName.equals("is" + property) + || methodName.equals("has" + property); + } + + /** + * Returns a setter that can be used for assigning the given design + * attribute to the class + * + * @since 7.4 + * @param clazz + * the class that is scanned for setters + * @param attribute + * the design attribute to find setter for + * @return the setter method or null if not found + */ + private static Method findSetterForAttribute(Class<?> clazz, + String attribute) { + String methodName = toSetterName(attribute); + for (Method method : clazz.getMethods()) { + if (method.getName().equals(methodName) + && method.getParameterTypes().length == 1 + && isSupported(method.getParameterTypes()[0])) { + return method; + } + } + getLogger().warning( + "Could not find setter with supported type for property " + + attribute); + return null; + } + + private static final List<Class<?>> supportedClasses = Arrays + .asList(new Class<?>[] { String.class, Boolean.class, + Integer.class, Byte.class, Short.class, Long.class, + Character.class, Float.class, Double.class, Locale.class, + Resource.class }); + + /** + * Returns true if the specified value type is supported by this class. + * Currently the handler supports primitives, {@link Locale.class} and + * {@link Resource.class}. + * + * @since 7.4 + * @param valueType + * the value type to be tested + * @return true if the value type is supported, otherwise false + */ + private static boolean isSupported(Class<?> valueType) { + return valueType != null + && (valueType.isPrimitive() || supportedClasses + .contains(valueType)); + } + + /** + * Searches for supported setter types from the specified class and returns + * the list of corresponding design attributes + * + * @since 7.4 + * @param clazz + * the class scanned for setters + * @return the list of supported design attributes + */ + public static List<String> findSupportedAttributes(Class<?> clazz) { + List<String> attributes = new ArrayList<String>(); + // TODO: should we check that we have the corresponding getter too? + // Otherwise we can not revert to default value. On the other hand, do + // we want that leaving the getter out will prevent reading the + // attribute from the design? + for (Method method : clazz.getMethods()) { + if (method.getName().startsWith("set") + && method.getParameterTypes().length == 1 + && isSupported(method.getParameterTypes()[0])) { + attributes.add(toAttributeName(method.getName())); + } + } + return attributes; + } + + /** + * Assigns the specified design attribute to the given component. If the + * attribute is not present, (value is null) the corresponding property is + * got from the <code>defaultInstance</code> + * + * @since 7.4 + * @param component + * the component to which the attribute should be set + * @param attribute + * the attribute to be set + * @param value + * the value for the attribute. If null, the corresponding + * property is got from the <code> defaultInstance</code> + * @param defaultInstance + * the default instance of the class for fetching the default + * values + */ + public static boolean assignAttribute(DesignSynchronizable component, + String attribute, String value, DesignSynchronizable defaultInstance) { + getLogger().info("Assigning attribute " + attribute + " -> " + value); + // find setter for the property + boolean success = false; + try { + Method setter = findSetterForAttribute(component.getClass(), + attribute); + if (setter == null) { + // if we don't have the setter, there is no point in continuing + success = false; + } else if (value != null) { + // we have a value from design attributes, let's use that + getLogger().info("Setting the value from attributes"); + Object param = parseAttributeValue( + setter.getParameterTypes()[0], value); + setter.invoke(component, param); + success = true; + } else { + // otherwise find the getter for the property + Method getter = findGetter(component.getClass(), setter); + if (getter != null) { + // read the default value from defaults + getLogger().info("Setting the default value"); + Object defaultValue = getter.invoke(defaultInstance); + setter.invoke(component, defaultValue); + success = true; + } + } + } catch (Exception e) { + getLogger().log(Level.WARNING, + "Failed to set attribute " + attribute, e); + } + if (!success) { + getLogger().info( + "property " + attribute + + " ignored by default attribute handler"); + } + return success; + } + + /** + * Assigns the width for the component based on the design attributes + * + * @since 7.4 + * @param component + * the component to assign the width + * @param attributes + * the attributes to be used for determining the width + * @param defaultInstance + * the default instance of the class for fetching the default + * value + */ + public static void assignWidth(DesignSynchronizable component, + Attributes attributes, DesignSynchronizable defaultInstance) { + if (attributes.hasKey("width-auto") || attributes.hasKey("size-auto")) { + component.setWidth(null); + } else if (attributes.hasKey("width-full") + || attributes.hasKey("size-full")) { + component.setWidth("100%"); + } else if (attributes.hasKey("width")) { + component.setWidth(attributes.get("width")); + } else { + component.setWidth(defaultInstance.getWidth(), + defaultInstance.getWidthUnits()); + } + } + + /** + * Assigns the height for the component based on the design attributes + * + * @since 7.4 + * @param component + * the component to assign the height + * @param attributes + * the attributes to be used for determining the height + * @param defaultInstance + * the default instance of the class for fetching the default + * value + */ + public static void assignHeight(DesignSynchronizable component, + Attributes attributes, DesignSynchronizable defaultInstance) { + if (attributes.hasKey("height-auto") || attributes.hasKey("size-auto")) { + component.setHeight(null); + } else if (attributes.hasKey("height-full") + || attributes.hasKey("size-full")) { + component.setHeight("100%"); + } else if (attributes.hasKey("height")) { + component.setHeight(attributes.get("height")); + } else { + component.setHeight(defaultInstance.getHeight(), + defaultInstance.getHeightUnits()); + } + } +} diff --git a/server/src/com/vaadin/ui/declarative/DesignContext.java b/server/src/com/vaadin/ui/declarative/DesignContext.java new file mode 100644 index 0000000000..4556e3a8f5 --- /dev/null +++ b/server/src/com/vaadin/ui/declarative/DesignContext.java @@ -0,0 +1,57 @@ +/* + * 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; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * This class contains contextual information that is collected when a component + * tree is constructed based on HTML design template + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class DesignContext { + + // cache for object instances + private static Map<Class<?>, Object> instanceCache = Collections + .synchronizedMap(new HashMap<Class<?>, Object>()); + + /** + * Returns the default instance for the given class. The instance must not + * be modified by the caller. + * + * @since + * @param instanceClass + * @return + */ + public <T> T getDefaultInstance(Class<T> instanceClass) { + T instance = (T) instanceCache.get(instanceClass); + if (instance == null) { + try { + instance = instanceClass.newInstance(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + instanceCache.put(instanceClass, instance); + } + return instance; + } +} 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 new file mode 100644 index 0000000000..1d8068f2a1 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/abstractcomponent/TestSynchronizeFromDesign.java @@ -0,0 +1,187 @@ +/* + * 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.abstractcomponent; + +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.server.ExternalResource; +import com.vaadin.server.FileResource; +import com.vaadin.server.ThemeResource; +import com.vaadin.ui.AbstractComponent; +import com.vaadin.ui.Label; +import com.vaadin.ui.declarative.DesignContext; + +/** + * Test case for the properties of the abstract component + * + * @since + * @author Vaadin Ltd + */ +public class TestSynchronizeFromDesign extends TestCase { + + private DesignContext ctx; + + @Override + protected void setUp() throws Exception { + super.setUp(); + ctx = new DesignContext(); + } + + public void testSynchronizeId() { + Node design = createDesign("id", "testId"); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals("testId", component.getId()); + } + + public void testSynchronizePrimaryStyleName() { + Node design = createDesign("primary-style-name", "test-style"); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals("test-style", component.getPrimaryStyleName()); + } + + public void testSynchronizeCaption() { + Node design = createDesign("caption", "test-caption"); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals("test-caption", component.getCaption()); + } + + public void testSynchronizeLocale() { + Node design = createDesign("locale", "fi"); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals("fi", component.getLocale().getLanguage()); + } + + public void testSynchronizeExternalIcon() { + Node design = createDesign("icon", "http://example.com/example.gif"); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertTrue("Incorrect resource type returned", component.getIcon() + .getClass().isAssignableFrom(ExternalResource.class)); + } + + public void testSynchronizeThemeIcon() { + Node design = createDesign("icon", "theme://example.gif"); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertTrue("Incorrect resource type returned", component.getIcon() + .getClass().isAssignableFrom(ThemeResource.class)); + } + + public void testSynchronizeFileResource() { + Node design = createDesign("icon", "img/example.gif"); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertTrue("Incorrect resource type returned", component.getIcon() + .getClass().isAssignableFrom(FileResource.class)); + } + + public void testSynchronizeImmediate() { + Node design = createDesign("immediate", "true"); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals(true, component.isImmediate()); + } + + public void testSynchronizeDescription() { + Node design = createDesign("description", "test-description"); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals("test-description", component.getDescription()); + } + + public void testSynchronizeSizeFull() { + Node design = createDesign("size-full", ""); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals(100, component.getWidth(), 0.1f); + assertEquals(100, component.getHeight(), 0.1f); + } + + public void testSynchronizeSizeAuto() { + Node design = createDesign("size-auto", ""); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals(-1, component.getWidth(), 0.1f); + assertEquals(-1, component.getHeight(), 0.1f); + } + + public void testSynchronizeHeightFull() { + Node design = createDesign("height-full", ""); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals(100, component.getHeight(), 0.1f); + } + + public void testSynchronizeHeightAuto() { + Node design = createDesign("height-auto", ""); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals(-1, component.getHeight(), 0.1f); + } + + public void testSynchronizeWidthFull() { + Node design = createDesign("width-full", ""); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals(100, component.getWidth(), 0.1f); + } + + public void testSynchronizeWidthAuto() { + Node design = createDesign("width-auto", ""); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals(-1, component.getWidth(), 0.1f); + } + + public void testSynchronizeWidth() { + Node design = createDesign("width", "12px"); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals(12, component.getWidth(), 0.1f); + assertEquals(com.vaadin.server.Sizeable.Unit.PIXELS, + component.getWidthUnits()); + } + + public void testSynchronizeHeight() { + Node design = createDesign("height", "12px"); + AbstractComponent component = getComponent(); + component.synchronizeFromDesign(design, ctx); + assertEquals(12, component.getHeight(), 0.1f); + assertEquals(com.vaadin.server.Sizeable.Unit.PIXELS, + component.getHeightUnits()); + + } + + private AbstractComponent getComponent() { + return new Label(); + } + + private Node createDesign(String key, String value) { + Attributes attributes = new Attributes(); + attributes.put(key, value); + Element node = new Element(Tag.valueOf("v-label"), "", attributes); + return node; + } +} |