summaryrefslogtreecommitdiffstats
path: root/server/src/com
diff options
context:
space:
mode:
authorMatti Hosio <mhosio@vaadin.com>2014-12-04 11:19:20 +0200
committerMatti Hosio <mhosio@vaadin.com>2014-12-04 13:00:28 +0200
commit9730b6def11af6f0bcb1e8b0535e0ec081ac84dd (patch)
tree8fc0165a79dc6e8f972bbc3bb6e0961dc19f13c3 /server/src/com
parent405ae14a6bc561fc1fc431929d9b132415a6b3bd (diff)
downloadvaadin-framework-9730b6def11af6f0bcb1e8b0535e0ec081ac84dd.tar.gz
vaadin-framework-9730b6def11af6f0bcb1e8b0535e0ec081ac84dd.zip
Declarative feature improvements (#7749)
Change-Id: Ie04db36ad08f686bf6b173241652836f639d3bd9
Diffstat (limited to 'server/src/com')
-rw-r--r--server/src/com/vaadin/ui/AbstractComponent.java213
-rw-r--r--server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java409
-rw-r--r--server/src/com/vaadin/ui/declarative/DesignContext.java12
3 files changed, 316 insertions, 318 deletions
diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java
index b1927f8580..b5721a0bba 100644
--- a/server/src/com/vaadin/ui/AbstractComponent.java
+++ b/server/src/com/vaadin/ui/AbstractComponent.java
@@ -19,13 +19,19 @@ package com.vaadin.ui;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
+import java.util.Set;
import java.util.StringTokenizer;
+import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.Attributes;
import org.jsoup.nodes.Element;
@@ -38,6 +44,7 @@ import com.vaadin.server.ComponentSizeValidator;
import com.vaadin.server.ErrorMessage;
import com.vaadin.server.ErrorMessage.ErrorLevel;
import com.vaadin.server.Resource;
+import com.vaadin.server.Sizeable;
import com.vaadin.server.UserError;
import com.vaadin.server.VaadinSession;
import com.vaadin.shared.AbstractComponentState;
@@ -922,8 +929,7 @@ public abstract class AbstractComponent extends AbstractClientConnector
DesignAttributeHandler.readAttribute(this, attribute, attr, def);
}
// handle width and height
- DesignAttributeHandler.readWidth(this, attr, def);
- DesignAttributeHandler.readHeight(this, attr, def);
+ readSize(attr, def);
// handle component error
if (attr.hasKey("error")) {
UserError error = new UserError(attr.get("error"),
@@ -932,28 +938,187 @@ public abstract class AbstractComponent extends AbstractClientConnector
} else {
setComponentError(def.getComponentError());
}
+ // check for unsupported attributes
+ Set<String> supported = new HashSet<String>();
+ supported.addAll(getDefaultAttributes());
+ supported.addAll(getCustomAttributes());
+ for (Attribute a : attr) {
+ if (!a.getKey().startsWith(":") && !supported.contains(a.getKey())) {
+ getLogger().info(
+ "Unsupported attribute found when synchronizing from design : "
+ + a.getKey());
+ }
+ }
+ }
+
+ /**
+ * Synchronizes the size of this component from the given design attributes.
+ * If the attributes do not contain relevant size information, defaults is
+ * consulted.
+ *
+ * @param attributes
+ * the design attributes
+ * @param defaultInstance
+ * instance of the class that has default sizing.
+ */
+ private void readSize(Attributes attributes,
+ AbstractComponent defaultInstance) {
+ // read width
+ if (attributes.hasKey("width-auto") || attributes.hasKey("size-auto")) {
+ this.setWidth(null);
+ } else if (attributes.hasKey("width-full")
+ || attributes.hasKey("size-full")) {
+ this.setWidth("100%");
+ } else if (attributes.hasKey("width")) {
+ this.setWidth(attributes.get("width"));
+ } else {
+ this.setWidth(defaultInstance.getWidth(),
+ defaultInstance.getWidthUnits());
+ }
+
+ // read height
+ if (attributes.hasKey("height-auto") || attributes.hasKey("size-auto")) {
+ this.setHeight(null);
+ } else if (attributes.hasKey("height-full")
+ || attributes.hasKey("size-full")) {
+ this.setHeight("100%");
+ } else if (attributes.hasKey("height")) {
+ this.setHeight(attributes.get("height"));
+ } else {
+ this.setHeight(defaultInstance.getHeight(),
+ defaultInstance.getHeightUnits());
+ }
+ }
+
+ /**
+ * Writes the size related attributes for the component if they differ from
+ * the defaults
+ *
+ * @param component
+ * the component
+ * @param attributes
+ * the attribute map where the attribute are written
+ * @param defaultInstance
+ * the default instance of the class for fetching the default
+ * values
+ */
+ private void writeSize(Attributes attributes,
+ DesignSynchronizable defaultInstance) {
+ if (hasEqualSize(defaultInstance)) {
+ // we have default values -> ignore
+ return;
+ }
+ boolean widthFull = getWidth() == 100f
+ && getWidthUnits().equals(Sizeable.Unit.PERCENTAGE);
+ boolean heightFull = getHeight() == 100f
+ && getHeightUnits().equals(Sizeable.Unit.PERCENTAGE);
+ boolean widthAuto = getWidth() == -1;
+ boolean heightAuto = getHeight() == -1;
+
+ // first try the full shorthands
+ if (widthFull && heightFull) {
+ attributes.put("size-full", "true");
+ } else if (widthAuto && heightAuto) {
+ attributes.put("size-auto", "true");
+ } else {
+ // handle width
+ if (!hasEqualWidth(defaultInstance)) {
+ if (widthFull) {
+ attributes.put("width-full", "true");
+ } else if (widthAuto) {
+ attributes.put("width-auto", "true");
+ } else {
+ String widthString = DesignAttributeHandler
+ .formatDesignAttribute(getWidth())
+ + getWidthUnits().getSymbol();
+ attributes.put("width", widthString);
+
+ }
+ }
+ if (!hasEqualHeight(defaultInstance)) {
+ // handle height
+ if (heightFull) {
+ attributes.put("height-full", "true");
+ } else if (heightAuto) {
+ attributes.put("height-auto", "true");
+ } else {
+ String heightString = DesignAttributeHandler
+ .formatDesignAttribute(getHeight())
+ + getHeightUnits().getSymbol();
+ attributes.put("height", heightString);
+ }
+ }
+ }
}
/**
- * Returns the list of attributes that do not require custom handling when
- * synchronizing from design. These are typically attributes of some
+ * Test if the given component has equal width with this instance
+ *
+ * @param component
+ * the component for the width comparison
+ * @return true if the widths are equal
+ */
+ private boolean hasEqualWidth(Component component) {
+ return getWidth() == component.getWidth()
+ && getWidthUnits().equals(component.getWidthUnits());
+ }
+
+ /**
+ * Test if the given component has equal height with this instance
+ *
+ * @param component
+ * the component for the height comparison
+ * @return true if the heights are equal
+ */
+ private boolean hasEqualHeight(Component component) {
+ return getHeight() == component.getHeight()
+ && getHeightUnits().equals(component.getHeightUnits());
+ }
+
+ /**
+ * Test if the given components has equal size with this instance
+ *
+ * @param component
+ * the component for the size comparison
+ * @return true if the sizes are equal
+ */
+ private boolean hasEqualSize(Component component) {
+ return hasEqualWidth(component) && hasEqualHeight(component);
+ }
+
+ /**
+ * Returns a collection 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 a collection of attributes that can be synchronized from design
+ * using the default approach.
+ */
+ private Collection<String> getDefaultAttributes() {
+ Collection<String> attributes = DesignAttributeHandler
+ .getSupportedAttributes(this.getClass());
+ attributes.removeAll(getCustomAttributes());
return attributes;
}
+ /**
+ * Returns a collection of attributes that should not be handled by the
+ * basic implementation of the {@link synhronizeFromDesign} and
+ * {@link synchronizeToDesign} methods. Typically these are handled in a
+ * custom way in the overridden versions of the above methods
+ *
+ * @return the collection of attributes that are not handled by the basic
+ * implementation
+ */
+ protected Collection<String> getCustomAttributes() {
+ return new ArrayList<String>(Arrays.asList(customAttributes));
+ }
+
+ private static final String[] customAttributes = new String[] { "width",
+ "height", "debug-id", "error", "width-auto", "height-auto",
+ "width-full", "height-full", "size-auto", "size-full" };
+
/*
* (non-Javadoc)
*
@@ -963,8 +1128,8 @@ public abstract class AbstractComponent extends AbstractClientConnector
*/
@Override
public void synchronizeToDesign(Element design, DesignContext designContext) {
- // clear node contents
- DesignAttributeHandler.clearNode(design);
+ // clear element contents
+ DesignAttributeHandler.clearElement(design);
AbstractComponent def = designContext.getDefaultInstance(this
.getClass());
Attributes attr = design.attributes();
@@ -973,10 +1138,14 @@ public abstract class AbstractComponent extends AbstractClientConnector
DesignAttributeHandler.writeAttribute(this, attribute, attr, def);
}
// handle size
- DesignAttributeHandler.writeSize(this, attr, def);
+ writeSize(attr, def);
// handle component error
- if (getComponentError() != null) {
- attr.put("error", getComponentError().getFormattedHtmlMessage());
+ String errorMsg = getComponentError() != null ? getComponentError()
+ .getFormattedHtmlMessage() : null;
+ String defErrorMsg = def.getComponentError() != null ? def
+ .getComponentError().getFormattedHtmlMessage() : null;
+ if (!SharedUtil.equals(errorMsg, defErrorMsg)) {
+ attr.put("error", errorMsg);
}
}
@@ -1096,4 +1265,8 @@ public abstract class AbstractComponent extends AbstractClientConnector
}
return false;
}
+
+ private static final Logger getLogger() {
+ return Logger.getLogger(AbstractComponent.class.getName());
+ }
}
diff --git a/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java b/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java
index 1e5ff47742..423b2fb60b 100644
--- a/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java
+++ b/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java
@@ -15,29 +15,37 @@
*/
package com.vaadin.ui.declarative;
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
import java.io.File;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang3.LocaleUtils;
import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.Attributes;
+import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import com.vaadin.server.ExternalResource;
import com.vaadin.server.FileResource;
import com.vaadin.server.FontAwesome;
import com.vaadin.server.Resource;
-import com.vaadin.server.Sizeable;
import com.vaadin.server.ThemeResource;
-import com.vaadin.ui.Component;
+import com.vaadin.shared.util.SharedUtil;
import com.vaadin.ui.DesignSynchronizable;
/**
@@ -54,14 +62,16 @@ public class DesignAttributeHandler {
return Logger.getLogger(DesignAttributeHandler.class.getName());
}
+ private static Map<Class, AttributeCacheEntry> cache = Collections
+ .synchronizedMap(new HashMap<Class, AttributeCacheEntry>());
+
/**
- * Clears the children and attributes of the given node
+ * Clears the children and attributes of the given element
*
- * @since 7.4
* @param design
- * the node to be cleared
+ * the element to be cleared
*/
- public static void clearNode(Node design) {
+ public static void clearElement(Element design) {
Attributes attr = design.attributes();
for (Attribute a : attr.asList()) {
attr.remove(a.getKey());
@@ -78,7 +88,6 @@ public class DesignAttributeHandler {
* 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
@@ -94,13 +103,15 @@ public class DesignAttributeHandler {
public static boolean readAttribute(DesignSynchronizable component,
String attribute, Attributes attributes,
DesignSynchronizable defaultInstance) {
-
String value = null;
+ if (component == null || attribute == null || attributes == null
+ || defaultInstance == null) {
+ throw new IllegalArgumentException(
+ "Parameters with null value not allowed");
+ }
if (attributes.hasKey(attribute)) {
value = attributes.get(attribute);
}
-
- // find setter for the property
boolean success = false;
try {
Method setter = findSetterForAttribute(component.getClass(),
@@ -115,15 +126,13 @@ public class DesignAttributeHandler {
setter.invoke(component, param);
success = true;
} else {
- // otherwise find the getter for the property
- Method getter = findGetterForSetter(component.getClass(),
- setter);
- if (getter != null) {
- // read the default value from defaults
- Object defaultValue = getter.invoke(defaultInstance);
- setter.invoke(component, defaultValue);
- success = true;
- }
+ // otherwise find the getter for the attribute
+ Method getter = findGetterForAttribute(component.getClass(),
+ attribute);
+ // read the default value from defaults
+ Object defaultValue = getter.invoke(defaultInstance);
+ setter.invoke(component, defaultValue);
+ success = true;
}
} catch (Exception e) {
getLogger().log(Level.WARNING,
@@ -141,86 +150,56 @@ public class DesignAttributeHandler {
* Searches for supported setter and getter 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>();
- for (Method method : clazz.getMethods()) {
- // check that the method is setter, has single argument of supported
- // type and has a corresponding getter
- if (method.getName().startsWith("set")
- && method.getParameterTypes().length == 1
- && isSupported(method.getParameterTypes()[0])
- && findGetterForSetter(clazz, method) != null) {
- attributes.add(toAttributeName(method.getName()));
- // TODO: we might want to cache the getters and setters?
- }
- }
- return attributes;
+ public static Collection<String> getSupportedAttributes(Class<?> clazz) {
+ resolveSupportedAttributes(clazz);
+ return cache.get(clazz).getAttributes();
}
/**
- * Assigns the width for the component based on the design attributes
+ * Resolves the supported attributes and corresponding getters and setters
+ * for the class using introspection. After resolving, the information is
+ * cached internally by this class
*
- * @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
+ * @param clazz
+ * the class to resolve the supported attributes for
*/
- public static void readWidth(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());
+ private static void resolveSupportedAttributes(Class<?> clazz) {
+ if (clazz == null) {
+ throw new IllegalArgumentException("The clazz can not be null");
}
- }
-
- /**
- * 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 readHeight(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());
+ if (cache.containsKey(clazz.getCanonicalName())) {
+ // NO-OP
+ return;
}
+ BeanInfo beanInfo;
+ try {
+ beanInfo = Introspector.getBeanInfo(clazz);
+ } catch (IntrospectionException e) {
+ throw new RuntimeException(
+ "Could not get supported attributes for class "
+ + clazz.getName());
+ }
+ AttributeCacheEntry entry = new AttributeCacheEntry();
+ for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
+ Method getter = descriptor.getReadMethod();
+ Method setter = descriptor.getWriteMethod();
+ if (getter != null && setter != null
+ && isSupported(descriptor.getPropertyType())) {
+ String attribute = toAttributeName(descriptor.getName());
+ entry.addAttribute(attribute, getter, setter);
+ }
+ }
+ cache.put(clazz, entry);
}
/**
* Writes the specified attribute to the design if it differs from the
* default value got from the <code> defaultInstance <code>
*
- * @since 7.4
* @param component
* the component used to get the attribute value
* @param attribute
@@ -243,9 +222,7 @@ public class DesignAttributeHandler {
Object value = getter.invoke(component);
Object defaultValue = getter.invoke(defaultInstance);
// if the values are not equal, write the data
- if (value == defaultValue
- || (value != null && value.equals(defaultValue))) {
- } else {
+ if (!SharedUtil.equals(value, defaultValue)) {
String attributeValue = toAttributeValue(
getter.getReturnType(), value);
attr.put(attribute, attributeValue);
@@ -260,69 +237,9 @@ public class DesignAttributeHandler {
}
/**
- * Writes the size related attributes for the component if they differ from
- * the defaults
- *
- * @since 7.4
- * @param component
- * the component
- * @param attributes
- * the attribute map where the attribute are written
- * @param defaultInstance
- * the default instance of the class for fetching the default
- * values
- */
- public static void writeSize(DesignSynchronizable component,
- Attributes attributes, DesignSynchronizable defaultInstance) {
- if (areEqualSize(component, defaultInstance)) {
- // we have default values -> ignore
- return;
- }
- boolean widthFull = component.getWidth() == 100f
- && component.getWidthUnits().equals(Sizeable.Unit.PERCENTAGE);
- boolean heightFull = component.getHeight() == 100f
- && component.getHeightUnits().equals(Sizeable.Unit.PERCENTAGE);
- boolean widthAuto = component.getWidth() == -1;
- boolean heightAuto = component.getHeight() == -1;
-
- // first try the full shorthands
- if (widthFull && heightFull) {
- attributes.put("size-full", "true");
- } else if (widthAuto && heightAuto) {
- attributes.put("size-auto", "true");
- } else {
- // handle width
- if (!areEqualWidth(component, defaultInstance)) {
- if (widthFull) {
- attributes.put("width-full", "true");
- } else if (widthAuto) {
- attributes.put("width-auto", "true");
- } else {
- attributes.put("width",
- formatDesignAttribute(component.getWidth())
- + component.getWidthUnits().getSymbol());
- }
- }
- if (!areEqualHeight(component, defaultInstance)) {
- // handle height
- if (heightFull) {
- attributes.put("height-full", "true");
- } else if (heightAuto) {
- attributes.put("height-auto", "true");
- } else {
- 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
@@ -340,16 +257,14 @@ public class DesignAttributeHandler {
* 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
+ * @param propertyName
+ * the property name returned by {@link IntroSpector}
* @return the design attribute name corresponding the given method name
*/
- private static String toAttributeName(String methodName) {
- String[] words = methodName.split("(?<!^)(?=[A-Z])");
+ private static String toAttributeName(String propertyName) {
+ String[] words = propertyName.split("(?<!^)(?=[A-Z])");
StringBuilder builder = new StringBuilder();
- // ignore first token ("set")
- for (int i = 1; i < words.length; i++) {
+ for (int i = 0; i < words.length; i++) {
if (builder.length() > 0) {
builder.append("-");
}
@@ -359,56 +274,8 @@ public class DesignAttributeHandler {
}
/**
- * 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();
- }
-
- /**
- * Returns a list of possible getter method names for the corresponding
- * design attribute name.
- *
- * @since 7.4
- * @param designAttributeName
- * the design attribute name
- * @return the list of getter method names corresponding the given design
- * attribute name
- */
- private static List<String> toGetterNames(String designAttributeName) {
- String[] parts = designAttributeName.split("-");
- StringBuilder builder = new StringBuilder();
- for (String part : parts) {
- builder.append(part.substring(0, 1).toUpperCase());
- builder.append(part.substring(1));
- }
- String propertyName = builder.toString();
- List<String> result = new ArrayList<String>();
- result.add("get" + propertyName);
- result.add("is" + propertyName);
- result.add("has" + propertyName);
- return result;
- }
-
- /**
* Parses the given attribute value to specified target type
*
- * @since 7.4
* @param targetType
* the target type for the value
* @param value
@@ -423,7 +290,7 @@ public class DesignAttributeHandler {
// 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");
+ return !value.equalsIgnoreCase("false");
}
if (targetType == Integer.TYPE || targetType == Integer.class) {
return Integer.valueOf(value);
@@ -457,9 +324,7 @@ public class DesignAttributeHandler {
/**
* Serializes the given value to valid design attribute representation
- * (string)
*
- * @since 7.4
* @param sourceType
* the type of the value
* @param value
@@ -467,6 +332,11 @@ public class DesignAttributeHandler {
* @return the given value as design attribute representation
*/
private static String toAttributeValue(Class<?> sourceType, Object value) {
+ if (value == null) {
+ // TODO: Handle corner case where sourceType is String and default
+ // value is not null. How to represent null value in attributes?
+ return "";
+ }
if (sourceType == Locale.class) {
return value != null ? ((Locale) value).toString() : null;
} else if (sourceType == Resource.class) {
@@ -485,6 +355,8 @@ public class DesignAttributeHandler {
return path;
}
} else {
+ getLogger().warning(
+ "Unknown resource type " + value.getClass().getName());
return null;
}
} else {
@@ -492,6 +364,13 @@ public class DesignAttributeHandler {
}
}
+ /**
+ * Parses the given attribute value as resource
+ *
+ * @param value
+ * the attribute value to be parsed
+ * @return resource instance based on the attribute value
+ */
private static Resource parseResource(String value) {
if (value.startsWith("http://")) {
return new ExternalResource("value");
@@ -505,40 +384,9 @@ public class DesignAttributeHandler {
}
/**
- * 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 findGetterForSetter(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;
- }
- }
- 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
@@ -547,25 +395,14 @@ public class DesignAttributeHandler {
*/
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;
+ resolveSupportedAttributes(clazz);
+ return cache.get(clazz).getSetter(attribute);
}
/**
- * Returns a getter that can be used for reading the value of the given
- * design attribute from the class
+ * Returns a getter that can be used for reading the given design attribute
+ * value from the class
*
- * @since 7.4
* @param clazz
* the class that is scanned for getters
* @param attribute
@@ -574,20 +411,11 @@ public class DesignAttributeHandler {
*/
private static Method findGetterForAttribute(Class<?> clazz,
String attribute) {
- List<String> methodNames = toGetterNames(attribute);
- for (Method method : clazz.getMethods()) {
- if (methodNames.contains(method.getName())
- && method.getParameterTypes().length == 0
- && isSupported(method.getReturnType())) {
- return method;
- }
- }
- getLogger().warning(
- "Could not find getter with supported return type for attribute "
- + attribute);
- return null;
+ resolveSupportedAttributes(clazz);
+ return cache.get(clazz).getGetter(attribute);
}
+ // supported property types
private static final List<Class<?>> supportedClasses = Arrays
.asList(new Class<?>[] { String.class, Boolean.class,
Integer.class, Byte.class, Short.class, Long.class,
@@ -599,7 +427,6 @@ public class DesignAttributeHandler {
* 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
@@ -611,41 +438,37 @@ public class DesignAttributeHandler {
}
/**
- * Test if the given components have equal width
+ * Cache object for caching supported attributes and their getters and
+ * setters
*
- * @since 7.4
- * @param comp1
- * @param comp2
- * @return true if the widths of the components are equal
+ * @author Vaadin Ltd
*/
- private static boolean areEqualWidth(Component comp1, Component comp2) {
- return comp1.getWidth() == comp2.getWidth()
- && comp1.getWidthUnits().equals(comp2.getWidthUnits());
- }
+ private static class AttributeCacheEntry {
+ private Map<String, Method[]> accessMethods = Collections
+ .synchronizedMap(new HashMap<String, Method[]>());
+
+ private void addAttribute(String attribute, Method getter, Method setter) {
+ Method[] methods = new Method[2];
+ methods[0] = getter;
+ methods[1] = setter;
+ accessMethods.put(attribute, methods);
+ }
- /**
- * Tests if the given components have equal height
- *
- * @since 7.4
- * @param comp1
- * @param comp2
- * @return true if the heights of the components are equal
- */
- private static boolean areEqualHeight(Component comp1, Component comp2) {
- return comp1.getHeight() == comp2.getHeight()
- && comp1.getHeightUnits().equals(comp2.getHeightUnits());
- }
+ private Collection<String> getAttributes() {
+ ArrayList<String> attributes = new ArrayList<String>();
+ attributes.addAll(accessMethods.keySet());
+ return attributes;
+ }
- /**
- * Test if the given components have equal size
- *
- * @since 7.4
- * @param comp1
- * @param comp2
- * @return true if the widht and height of the components are equal
- */
- private static boolean areEqualSize(Component comp1, Component comp2) {
- return areEqualWidth(comp1, comp2) && areEqualHeight(comp1, comp2);
+ private Method getGetter(String attribute) {
+ Method[] methods = accessMethods.get(attribute);
+ return (methods != null && methods.length > 0) ? methods[0] : null;
+ }
+
+ private Method getSetter(String attribute) {
+ Method[] methods = accessMethods.get(attribute);
+ return (methods != null && methods.length > 1) ? methods[1] : null;
+ }
}
}
diff --git a/server/src/com/vaadin/ui/declarative/DesignContext.java b/server/src/com/vaadin/ui/declarative/DesignContext.java
index 1af8fc4fcd..961affd5b0 100644
--- a/server/src/com/vaadin/ui/declarative/DesignContext.java
+++ b/server/src/com/vaadin/ui/declarative/DesignContext.java
@@ -215,21 +215,23 @@ public class DesignContext {
* Returns the default instance for the given class. The instance must not
* be modified by the caller.
*
- * @since
* @param instanceClass
- * @return
+ * @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);
if (instance == null) {
try {
instance = instanceClass.newInstance();
+ instanceCache.put(instanceClass, instance);
} catch (InstantiationException e) {
- e.printStackTrace();
+ throw new RuntimeException("Could not instantiate "
+ + instanceClass.getName());
} catch (IllegalAccessException e) {
- e.printStackTrace();
+ throw new RuntimeException("Could not instantiate "
+ + instanceClass.getName());
}
- instanceCache.put(instanceClass, instance);
}
return instance;
}