aboutsummaryrefslogtreecommitdiffstats
path: root/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java')
-rw-r--r--server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java408
1 files changed, 63 insertions, 345 deletions
diff --git a/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java b/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java
index be7d023ebf..3e2c01c881 100644
--- a/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java
+++ b/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java
@@ -19,35 +19,25 @@ import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
-import java.io.File;
import java.io.Serializable;
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 java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.Attributes;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
-import com.vaadin.event.ShortcutAction;
-import com.vaadin.event.ShortcutAction.KeyCode;
-import com.vaadin.event.ShortcutAction.ModifierKey;
-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.data.util.converter.Converter;
import com.vaadin.shared.util.SharedUtil;
import com.vaadin.ui.Component;
@@ -65,8 +55,21 @@ public class DesignAttributeHandler implements Serializable {
return Logger.getLogger(DesignAttributeHandler.class.getName());
}
- private static Map<Class, AttributeCacheEntry> cache = Collections
- .synchronizedMap(new HashMap<Class, AttributeCacheEntry>());
+ private static Map<Class<?>, AttributeCacheEntry> cache = Collections
+ .synchronizedMap(new HashMap<Class<?>, AttributeCacheEntry>());
+
+ // translates string <-> object
+ private static DesignFormatter FORMATTER = new DesignFormatter();
+
+ /**
+ * Returns the currently used formatter. All primitive types and all types
+ * needed by Vaadin components are handled by that formatter.
+ *
+ * @return An instance of the formatter.
+ */
+ public static DesignFormatter getFormatter() {
+ return FORMATTER;
+ }
/**
* Clears the children and attributes of the given element
@@ -111,8 +114,8 @@ public class DesignAttributeHandler implements Serializable {
success = false;
} else {
// we have a value from design attributes, let's use that
- Object param = fromAttributeValue(
- setter.getParameterTypes()[0], value);
+ Object param = getFormatter().parse(value,
+ setter.getParameterTypes()[0]);
setter.invoke(target, param);
success = true;
}
@@ -170,7 +173,7 @@ public class DesignAttributeHandler implements Serializable {
Method getter = descriptor.getReadMethod();
Method setter = descriptor.getWriteMethod();
if (getter != null && setter != null
- && isSupported(descriptor.getPropertyType())) {
+ && getFormatter().canConvert(descriptor.getPropertyType())) {
String attribute = toAttributeName(descriptor.getName());
entry.addAttribute(attribute, getter, setter);
}
@@ -229,10 +232,9 @@ public class DesignAttributeHandler implements Serializable {
* @return the attribute value or the default value if the attribute is not
* found
*/
- @SuppressWarnings("unchecked")
public static <T> T readAttribute(String attribute, Attributes attributes,
Class<T> outputType) {
- if (!isSupported(outputType)) {
+ if (!getFormatter().canConvert(outputType)) {
throw new IllegalArgumentException("output type: "
+ outputType.getName() + " not supported");
}
@@ -241,7 +243,7 @@ public class DesignAttributeHandler implements Serializable {
} else {
try {
String value = attributes.get(attribute);
- return (T) fromAttributeValue(outputType, value);
+ return getFormatter().parse(value, outputType);
} catch (Exception e) {
throw new DesignException("Failed to read attribute "
+ attribute, e);
@@ -266,7 +268,7 @@ public class DesignAttributeHandler implements Serializable {
*/
public static <T> void writeAttribute(String attribute,
Attributes attributes, T value, T defaultValue, Class<T> inputType) {
- if (!isSupported(inputType)) {
+ if (!getFormatter().canConvert(inputType)) {
throw new IllegalArgumentException("input type: "
+ inputType.getName() + " not supported");
}
@@ -277,101 +279,6 @@ public class DesignAttributeHandler implements Serializable {
}
/**
- * Formats the given design attribute value. The method is provided to
- * ensure consistent number formatting for design attribute values
- *
- * @param number
- * the number to be formatted
- * @return the formatted number
- */
- public static String formatFloat(float number) {
- return getDecimalFormat().format(number);
- }
-
- /**
- * Formats the given design attribute value. The method is provided to
- * ensure consistent number formatting for design attribute values
- *
- * @param number
- * the number to be formatted
- * @return the formatted number
- */
- public static String formatDouble(double number) {
- return getDecimalFormat().format(number);
- }
-
- /**
- * Convert ShortcutAction to attribute string presentation
- *
- * @param shortcut
- * the shortcut action
- * @return the action as attribute string presentation
- */
- private static String formatShortcutAction(ShortcutAction shortcut) {
- StringBuilder sb = new StringBuilder();
- // handle modifiers
- if (shortcut.getModifiers() != null) {
- for (int modifier : shortcut.getModifiers()) {
- sb.append(ShortcutKeyMapper.getStringForKeycode(modifier))
- .append("-");
- }
- }
- // handle keycode
- sb.append(ShortcutKeyMapper.getStringForKeycode(shortcut.getKeyCode()));
- return sb.toString();
- }
-
- /**
- * Reads shortcut action from attribute presentation
- *
- * @param attributeValue
- * attribute presentation of shortcut action
- * @return shortcut action with keycode and modifier keys from attribute
- * value
- */
- private static ShortcutAction readShortcutAction(String attributeValue) {
- if (attributeValue.length() == 0) {
- return null;
- }
- String[] parts = attributeValue.split("-");
- // handle keycode
- String keyCodePart = parts[parts.length - 1];
- int keyCode = ShortcutKeyMapper.getKeycodeForString(keyCodePart);
- if (keyCode < 0) {
- throw new IllegalArgumentException("Invalid shortcut definition "
- + attributeValue);
- }
- // handle modifiers
- int[] modifiers = null;
- if (parts.length > 1) {
- modifiers = new int[parts.length - 1];
- }
- for (int i = 0; i < parts.length - 1; i++) {
- int modifier = ShortcutKeyMapper.getKeycodeForString(parts[i]);
- if (modifier > 0) {
- modifiers[i] = modifier;
- } else {
- throw new IllegalArgumentException(
- "Invalid shortcut definition " + attributeValue);
- }
- }
- return new ShortcutAction(null, keyCode, modifiers);
- }
-
- /**
- * Creates the decimal format used when writing attributes to the design.
- *
- * @return the decimal format
- */
- private static DecimalFormat getDecimalFormat() {
- DecimalFormatSymbols symbols = new DecimalFormatSymbols(new Locale(
- "en_US"));
- DecimalFormat fmt = new DecimalFormat("0.###", symbols);
- fmt.setGroupingUsed(false);
- return fmt;
- }
-
- /**
* 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>
@@ -381,6 +288,7 @@ public class DesignAttributeHandler implements Serializable {
* @return the design attribute name corresponding the given method name
*/
private static String toAttributeName(String propertyName) {
+ propertyName = removeSubsequentUppercase(propertyName);
String[] words = propertyName.split("(?<!^)(?=[A-Z])");
StringBuilder builder = new StringBuilder();
for (int i = 0; i < words.length; i++) {
@@ -393,56 +301,43 @@ public class DesignAttributeHandler implements Serializable {
}
/**
- * Parses the given attribute value to specified target type
+ * Replaces subsequent UPPERCASE strings of length 2 or more followed either
+ * by another uppercase letter or an end of string. This is to generalise
+ * handling of method names like <tt>showISOWeekNumbers</tt>.
*
- * @param targetType
- * the target type for the value
- * @param value
- * the parsed value
- * @return the object of specified target type
+ * @param param
+ * Input string.
+ * @return Input string with sequences of UPPERCASE turned into Normalcase.
*/
- private static Object fromAttributeValue(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 parseBoolean(value);
- }
- 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 == Resource.class) {
- return parseResource(value);
- }
- if (Enum.class.isAssignableFrom(targetType)) {
- return Enum.valueOf((Class<? extends Enum>) targetType,
- value.toUpperCase());
- }
- if (targetType == ShortcutAction.class) {
- return readShortcutAction(value);
+ private static String removeSubsequentUppercase(String param) {
+ StringBuffer result = new StringBuffer();
+ // match all two-or-more caps letters lead by a non-uppercase letter
+ // followed by either a capital letter or string end
+ Pattern pattern = Pattern.compile("(^|[^A-Z])([A-Z]{2,})([A-Z]|$)");
+ Matcher matcher = pattern.matcher(param);
+ while (matcher.find()) {
+ String matched = matcher.group(2);
+ // if this is a beginning of the string, the whole matched group is
+ // written in lower case
+ if (matcher.group(1).isEmpty()) {
+ matcher.appendReplacement(result, matched.toLowerCase()
+ + matcher.group(3));
+ // otherwise the first character of the group stays uppercase,
+ // while the others are lower case
+ } else {
+ matcher.appendReplacement(
+ result,
+ matcher.group(1) + matched.substring(0, 1)
+ + matched.substring(1).toLowerCase()
+ + matcher.group(3));
+ }
+ // in both cases the uppercase letter of the next word (or string's
+ // end) is added
+ // this implies there is at least one extra lowercase letter after
+ // it to be caught by the next call to find()
}
- return null;
+ matcher.appendTail(result);
+ return result.toString();
}
/**
@@ -460,57 +355,16 @@ public class DesignAttributeHandler implements Serializable {
// value is not null. How to represent null value in attributes?
return "";
}
- if (sourceType == Resource.class) {
- if (value instanceof ExternalResource) {
- return ((ExternalResource) value).getURL();
- } else if (value instanceof ThemeResource) {
- return "theme://" + ((ThemeResource) value).getResourceId();
- } else if (value instanceof FontAwesome) {
- return "font://" + ((FontAwesome) value).name();
- } else if (value instanceof FileResource) {
- String path = ((FileResource) value).getSourceFile().getPath();
- if (File.separatorChar != '/') {
- // make sure we use '/' as file separator in templates
- return path.replace(File.separatorChar, '/');
- } else {
- return path;
- }
- } else {
- getLogger().warning(
- "Unknown resource type " + value.getClass().getName());
- return null;
- }
- } else if (sourceType == Float.class || sourceType == Float.TYPE) {
- return formatFloat(((Float) value).floatValue());
- } else if (sourceType == Double.class || sourceType == Double.TYPE) {
- return formatDouble(((Double) value).doubleValue());
- } else if (sourceType == ShortcutAction.class) {
- return formatShortcutAction((ShortcutAction) value);
+ Converter<String, Object> converter = getFormatter().findConverterFor(
+ sourceType);
+ if (converter != null) {
+ return converter.convertToPresentation(value, String.class, null);
} else {
return value.toString();
}
}
/**
- * 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);
- } 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));
- }
- }
-
- /**
* Returns a setter that can be used for assigning the given design
* attribute to the class
*
@@ -542,29 +396,6 @@ public class DesignAttributeHandler implements Serializable {
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,
- Character.class, Float.class, Double.class, Resource.class,
- ShortcutAction.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}.
- *
- * @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) || Enum.class
- .isAssignableFrom(valueType));
- }
-
/**
* Cache object for caching supported attributes and their getters and
* setters
@@ -599,117 +430,4 @@ public class DesignAttributeHandler implements Serializable {
}
}
- /**
- * Provides mappings between shortcut keycodes and their representation in
- * design attributes
- *
- * @author Vaadin Ltd
- */
- private static class ShortcutKeyMapper implements Serializable {
-
- private static Map<Integer, String> keyCodeMap = Collections
- .synchronizedMap(new HashMap<Integer, String>());
- private static Map<String, Integer> presentationMap = Collections
- .synchronizedMap(new HashMap<String, Integer>());
-
- static {
- // map modifiers
- mapKey(ModifierKey.ALT, "alt");
- mapKey(ModifierKey.CTRL, "ctrl");
- mapKey(ModifierKey.META, "meta");
- mapKey(ModifierKey.SHIFT, "shift");
- // map keys
- mapKey(KeyCode.ENTER, "enter");
- mapKey(KeyCode.ESCAPE, "escape");
- mapKey(KeyCode.PAGE_UP, "pageup");
- mapKey(KeyCode.PAGE_DOWN, "pagedown");
- mapKey(KeyCode.TAB, "tab");
- mapKey(KeyCode.ARROW_LEFT, "left");
- mapKey(KeyCode.ARROW_UP, "up");
- mapKey(KeyCode.ARROW_RIGHT, "right");
- mapKey(KeyCode.ARROW_DOWN, "down");
- mapKey(KeyCode.BACKSPACE, "backspace");
- mapKey(KeyCode.DELETE, "delete");
- mapKey(KeyCode.INSERT, "insert");
- mapKey(KeyCode.END, "end");
- mapKey(KeyCode.HOME, "home");
- mapKey(KeyCode.F1, "f1");
- mapKey(KeyCode.F2, "f2");
- mapKey(KeyCode.F3, "f3");
- mapKey(KeyCode.F4, "f4");
- mapKey(KeyCode.F5, "f5");
- mapKey(KeyCode.F6, "f6");
- mapKey(KeyCode.F7, "f7");
- mapKey(KeyCode.F8, "f8");
- mapKey(KeyCode.F9, "f9");
- mapKey(KeyCode.F10, "f10");
- mapKey(KeyCode.F11, "f11");
- mapKey(KeyCode.F12, "f12");
- mapKey(KeyCode.NUM0, "0");
- mapKey(KeyCode.NUM1, "1");
- mapKey(KeyCode.NUM2, "2");
- mapKey(KeyCode.NUM3, "3");
- mapKey(KeyCode.NUM4, "4");
- mapKey(KeyCode.NUM5, "5");
- mapKey(KeyCode.NUM6, "6");
- mapKey(KeyCode.NUM7, "7");
- mapKey(KeyCode.NUM8, "8");
- mapKey(KeyCode.NUM9, "9");
- mapKey(KeyCode.SPACEBAR, "spacebar");
- mapKey(KeyCode.A, "a");
- mapKey(KeyCode.B, "b");
- mapKey(KeyCode.C, "c");
- mapKey(KeyCode.D, "d");
- mapKey(KeyCode.E, "e");
- mapKey(KeyCode.F, "f");
- mapKey(KeyCode.G, "g");
- mapKey(KeyCode.H, "h");
- mapKey(KeyCode.I, "i");
- mapKey(KeyCode.J, "j");
- mapKey(KeyCode.K, "k");
- mapKey(KeyCode.L, "l");
- mapKey(KeyCode.M, "m");
- mapKey(KeyCode.N, "n");
- mapKey(KeyCode.O, "o");
- mapKey(KeyCode.P, "p");
- mapKey(KeyCode.Q, "q");
- mapKey(KeyCode.R, "r");
- mapKey(KeyCode.S, "s");
- mapKey(KeyCode.T, "t");
- mapKey(KeyCode.U, "u");
- mapKey(KeyCode.V, "v");
- mapKey(KeyCode.X, "x");
- mapKey(KeyCode.Y, "y");
- mapKey(KeyCode.Z, "z");
- }
-
- private static void mapKey(int keyCode, String presentation) {
- keyCodeMap.put(keyCode, presentation);
- presentationMap.put(presentation, keyCode);
- }
-
- private static int getKeycodeForString(String attributePresentation) {
- Integer code = presentationMap.get(attributePresentation);
- return code != null ? code.intValue() : -1;
- }
-
- private static String getStringForKeycode(int keyCode) {
- return keyCodeMap.get(keyCode);
- }
- }
-
- /**
- * Converts the given string attribute value to its corresponding boolean.
- *
- * An empty string and "true" are considered to represent a true value and
- * "false" to represent a false value.
- *
- * @param booleanValue
- * the boolean value from an attribute
- * @return the parsed boolean
- */
- public static boolean parseBoolean(String booleanValue) {
- return !booleanValue.equalsIgnoreCase("false");
- }
-
} \ No newline at end of file