diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml index dae3ae741..682d4122b 100644 --- a/core/src/main/resources/struts-default.xml +++ b/core/src/main/resources/struts-default.xml @@ -63,6 +63,12 @@ + + + + + + diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/ObjectFactory.java b/xwork-core/src/main/java/com/opensymphony/xwork2/ObjectFactory.java index 56f7ac2b9..c06373b81 100644 --- a/xwork-core/src/main/java/com/opensymphony/xwork2/ObjectFactory.java +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/ObjectFactory.java @@ -259,15 +259,48 @@ public class ObjectFactory implements Serializable { /** * Build converter of given type - it must be registered with {@link Container} first - * It's the first attempt to use Object Factory to build Converters, so the API can change in the future - * + * * @param converterClass to instantiate * @return instance of converterClass with inject dependencies */ public TypeConverter buildConverter(Class converterClass) { + if (LOG.isDebugEnabled()) { + LOG.debug("Creating converter of type [#1]", converterClass.getCanonicalName()); + } return container.getInstance(converterClass); } + /** + * Build converter of given type - it must be registered with {@link Container} first + * + * @param converterClass to instantiate + * @param name name of converter to use + * @return instance of converterClass with inject dependencies + */ + public TypeConverter buildConverter(Class converterClass, String name) { + if (LOG.isDebugEnabled()) { + LOG.debug("Creating converter of type [#1] with name [#2]", converterClass.getCanonicalName(), name); + } + return container.getInstance(converterClass, name); + } + + /** + * Build converter of given type - it must be registered with {@link Container} first + * + * @param name name of converter to use + * @return instance of converterClass with inject dependencies + */ + public TypeConverter buildConverter(String name) { + if (LOG.isDebugEnabled()) { + LOG.debug("Creating converter with name [#1]", name); + } + TypeConverter instance = container.getInstance(TypeConverter.class, name); + if (LOG.isDebugEnabled()) { + LOG.debug("Converter of Type [#1] with name [#2], created!", instance.getClass().getCanonicalName(), name); + } + return instance; + } + static class ContinuationsClassLoader extends ClassLoader { } diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java b/xwork-core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java index 627e4411c..82b234648 100644 --- a/xwork-core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java @@ -19,14 +19,34 @@ import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.DefaultTextProvider; import com.opensymphony.xwork2.ObjectFactory; import com.opensymphony.xwork2.TextProvider; -import com.opensymphony.xwork2.config.*; -import com.opensymphony.xwork2.config.entities.*; +import com.opensymphony.xwork2.config.Configuration; +import com.opensymphony.xwork2.config.ConfigurationException; +import com.opensymphony.xwork2.config.ConfigurationProvider; +import com.opensymphony.xwork2.config.ContainerProvider; +import com.opensymphony.xwork2.config.PackageProvider; +import com.opensymphony.xwork2.config.RuntimeConfiguration; +import com.opensymphony.xwork2.config.entities.ActionConfig; +import com.opensymphony.xwork2.config.entities.InterceptorMapping; +import com.opensymphony.xwork2.config.entities.PackageConfig; +import com.opensymphony.xwork2.config.entities.ResultConfig; +import com.opensymphony.xwork2.config.entities.ResultTypeConfig; +import com.opensymphony.xwork2.config.entities.UnknownHandlerConfig; import com.opensymphony.xwork2.config.providers.InterceptorBuilder; import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer; +import com.opensymphony.xwork2.conversion.TypeConverter; +import com.opensymphony.xwork2.conversion.impl.ArrayConverter; +import com.opensymphony.xwork2.conversion.impl.CollectionConverter; +import com.opensymphony.xwork2.conversion.impl.DateConverter; import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer; +import com.opensymphony.xwork2.conversion.impl.NumberConverter; +import com.opensymphony.xwork2.conversion.impl.StringConverter; import com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter; import com.opensymphony.xwork2.conversion.impl.XWorkConverter; -import com.opensymphony.xwork2.inject.*; +import com.opensymphony.xwork2.inject.Container; +import com.opensymphony.xwork2.inject.ContainerBuilder; +import com.opensymphony.xwork2.inject.Context; +import com.opensymphony.xwork2.inject.Factory; +import com.opensymphony.xwork2.inject.Scope; import com.opensymphony.xwork2.ognl.OgnlReflectionProvider; import com.opensymphony.xwork2.ognl.OgnlUtil; import com.opensymphony.xwork2.ognl.OgnlValueStackFactory; @@ -41,7 +61,13 @@ import com.opensymphony.xwork2.util.logging.LoggerFactory; import com.opensymphony.xwork2.util.reflection.ReflectionProvider; import ognl.PropertyAccessor; -import java.util.*; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; /** @@ -243,6 +269,11 @@ public class DefaultConfiguration implements Configuration { builder.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON); builder.factory(XWorkConverter.class, Scope.SINGLETON); builder.factory(XWorkBasicConverter.class, Scope.SINGLETON); + builder.factory(TypeConverter.class, "collection", CollectionConverter.class, Scope.SINGLETON); + builder.factory(TypeConverter.class, "array", ArrayConverter.class, Scope.SINGLETON); + builder.factory(TypeConverter.class, "date", DateConverter.class, Scope.SINGLETON); + builder.factory(TypeConverter.class, "number", NumberConverter.class, Scope.SINGLETON); + builder.factory(TypeConverter.class, "string", StringConverter.class, Scope.SINGLETON); builder.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON); builder.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON); builder.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON); diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/config/providers/XWorkConfigurationProvider.java b/xwork-core/src/main/java/com/opensymphony/xwork2/config/providers/XWorkConfigurationProvider.java index bd3539860..b6068c803 100644 --- a/xwork-core/src/main/java/com/opensymphony/xwork2/config/providers/XWorkConfigurationProvider.java +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/config/providers/XWorkConfigurationProvider.java @@ -12,8 +12,14 @@ import com.opensymphony.xwork2.config.ConfigurationException; import com.opensymphony.xwork2.config.ConfigurationProvider; import com.opensymphony.xwork2.conversion.NullHandler; import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer; +import com.opensymphony.xwork2.conversion.TypeConverter; +import com.opensymphony.xwork2.conversion.impl.ArrayConverter; +import com.opensymphony.xwork2.conversion.impl.CollectionConverter; +import com.opensymphony.xwork2.conversion.impl.DateConverter; import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer; import com.opensymphony.xwork2.conversion.impl.InstantiatingNullHandler; +import com.opensymphony.xwork2.conversion.impl.NumberConverter; +import com.opensymphony.xwork2.conversion.impl.StringConverter; import com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter; import com.opensymphony.xwork2.conversion.impl.XWorkConverter; import com.opensymphony.xwork2.inject.ContainerBuilder; @@ -111,7 +117,12 @@ public class XWorkConfigurationProvider implements ConfigurationProvider { .factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON) .factory(TextProvider.class, TextProviderSupport.class, Scope.SINGLETON) .factory(OgnlUtil.class, Scope.SINGLETON) - .factory(XWorkBasicConverter.class, Scope.SINGLETON); + .factory(XWorkBasicConverter.class, Scope.SINGLETON) + .factory(TypeConverter.class, "collection", CollectionConverter.class, Scope.SINGLETON) + .factory(TypeConverter.class, "array", ArrayConverter.class, Scope.SINGLETON) + .factory(TypeConverter.class, "date", DateConverter.class, Scope.SINGLETON) + .factory(TypeConverter.class, "number", NumberConverter.class, Scope.SINGLETON) + .factory(TypeConverter.class, "string", StringConverter.class, Scope.SINGLETON); props.setProperty("devMode", Boolean.FALSE.toString()); props.setProperty("logMissingProperties", Boolean.FALSE.toString()); props.setProperty("enableOGNLExpressionCache", Boolean.TRUE.toString()); diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/ArrayConverter.java b/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/ArrayConverter.java new file mode 100644 index 000000000..09addae85 --- /dev/null +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/ArrayConverter.java @@ -0,0 +1,36 @@ +package com.opensymphony.xwork2.conversion.impl; + +import com.opensymphony.xwork2.conversion.TypeConverter; + +import java.lang.reflect.Array; +import java.lang.reflect.Member; +import java.util.Map; + +public class ArrayConverter extends DefaultTypeConverter { + + @Override + public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType) { + Object result = null; + Class componentType = toType.getComponentType(); + + if (componentType != null) { + TypeConverter converter = getTypeConverter(context); + + if (value.getClass().isArray()) { + int length = Array.getLength(value); + result = Array.newInstance(componentType, length); + + for (int i = 0; i < length; i++) { + Object valueItem = Array.get(value, i); + Array.set(result, i, converter.convertValue(context, target, member, propertyName, valueItem, componentType)); + } + } else { + result = Array.newInstance(componentType, 1); + Array.set(result, 0, converter.convertValue(context, target, member, propertyName, value, componentType)); + } + } + + return result; + } + +} diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/CollectionConverter.java b/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/CollectionConverter.java new file mode 100644 index 000000000..19c2cc5bb --- /dev/null +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/CollectionConverter.java @@ -0,0 +1,101 @@ +package com.opensymphony.xwork2.conversion.impl; + +import com.opensymphony.xwork2.ObjectFactory; +import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer; +import com.opensymphony.xwork2.conversion.TypeConverter; +import com.opensymphony.xwork2.inject.Inject; +import com.opensymphony.xwork2.util.XWorkList; + +import java.lang.reflect.Member; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +public class CollectionConverter extends DefaultTypeConverter { + + private ObjectTypeDeterminer objectTypeDeterminer; + private ObjectFactory objectFactory; + + @Inject + public void setObjectTypeDeterminer(ObjectTypeDeterminer determiner) { + this.objectTypeDeterminer = determiner; + } + + @Inject + public void setObjectFactory(ObjectFactory factory) { + this.objectFactory = factory; + } + + public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType) { + Collection result; + Class memberType = String.class; + + if (target != null) { + //memberType = (Class) XWorkConverter.getInstance().getConverter(o.getClass(), XWorkConverter.CONVERSION_COLLECTION_PREFIX + prop); + memberType = objectTypeDeterminer.getElementClass(target.getClass(), propertyName, null); + + if (memberType == null) { + memberType = String.class; + } + } + + if (toType.isAssignableFrom(value.getClass())) { + // no need to do anything + result = (Collection) value; + } else if (value.getClass().isArray()) { + Object[] objArray = (Object[]) value; + TypeConverter converter = getTypeConverter(context); + result = createCollection(toType, memberType, objArray.length); + + for (Object anObjArray : objArray) { + Object convertedValue = converter.convertValue(context, target, member, propertyName, anObjArray, memberType); + if (!TypeConverter.NO_CONVERSION_POSSIBLE.equals(convertedValue)) { + result.add(convertedValue); + } + } + } else if (Collection.class.isAssignableFrom(value.getClass())) { + Collection col = (Collection) value; + TypeConverter converter = getTypeConverter(context); + result = createCollection(toType, memberType, col.size()); + + for (Object aCol : col) { + Object convertedValue = converter.convertValue(context, target, member, propertyName, aCol, memberType); + if (!TypeConverter.NO_CONVERSION_POSSIBLE.equals(convertedValue)) { + result.add(convertedValue); + } + } + } else { + result = createCollection(toType, memberType, -1); + result.add(value); + } + + return result; + + } + + private Collection createCollection(Class toType, Class memberType, int size) { + Collection result; + + if (toType == Set.class) { + if (size > 0) { + result = new HashSet(size); + } else { + result = new HashSet(); + } + } else if (toType == SortedSet.class) { + result = new TreeSet(); + } else { + if (size > 0) { + result = new XWorkList(objectFactory, memberType, size); + } else { + result = new XWorkList(objectFactory, memberType); + } + } + + return result; + } + +} diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/DateConverter.java b/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/DateConverter.java new file mode 100644 index 000000000..99b1558dc --- /dev/null +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/DateConverter.java @@ -0,0 +1,101 @@ +package com.opensymphony.xwork2.conversion.impl; + +import com.opensymphony.xwork2.XWorkException; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Member; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Map; + +public class DateConverter extends DefaultTypeConverter { + + @Override + public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType) { + Date result = null; + + if (value instanceof String && value != null && ((String) value).length() > 0) { + String sa = (String) value; + Locale locale = getLocale(context); + + DateFormat df = null; + if (java.sql.Time.class == toType) { + df = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale); + } else if (java.sql.Timestamp.class == toType) { + Date check = null; + SimpleDateFormat dtfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, + DateFormat.MEDIUM, + locale); + SimpleDateFormat fullfmt = new SimpleDateFormat(dtfmt.toPattern() + MILLISECOND_FORMAT, + locale); + + SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, + locale); + + SimpleDateFormat[] fmts = {fullfmt, dtfmt, dfmt}; + for (SimpleDateFormat fmt : fmts) { + try { + check = fmt.parse(sa); + df = fmt; + if (check != null) { + break; + } + } catch (ParseException ignore) { + } + } + } else if (java.util.Date.class == toType) { + Date check; + DateFormat[] dfs = getDateFormats(locale); + for (DateFormat df1 : dfs) { + try { + check = df1.parse(sa); + df = df1; + if (check != null) { + break; + } + } catch (ParseException ignore) { + } + } + } + //final fallback for dates without time + if (df == null) { + df = DateFormat.getDateInstance(DateFormat.SHORT, locale); + } + try { + df.setLenient(false); // let's use strict parsing (XW-341) + result = df.parse(sa); + if (!(Date.class == toType)) { + try { + Constructor constructor = toType.getConstructor(new Class[]{long.class}); + return constructor.newInstance(new Object[]{Long.valueOf(result.getTime())}); + } catch (Exception e) { + throw new XWorkException("Couldn't create class " + toType + " using default (long) constructor", e); + } + } + } catch (ParseException e) { + throw new XWorkException("Could not parse date", e); + } + } else if (Date.class.isAssignableFrom(value.getClass())) { + result = (Date) value; + } + return result; + } + + private DateFormat[] getDateFormats(Locale locale) { + DateFormat dt1 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG, locale); + DateFormat dt2 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale); + DateFormat dt3 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale); + + DateFormat d1 = DateFormat.getDateInstance(DateFormat.SHORT, locale); + DateFormat d2 = DateFormat.getDateInstance(DateFormat.MEDIUM, locale); + DateFormat d3 = DateFormat.getDateInstance(DateFormat.LONG, locale); + + DateFormat rfc3399 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + + return new DateFormat[]{dt1, dt2, dt3, rfc3399, d1, d2, d3}; + } + +} diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/DefaultTypeConverter.java b/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/DefaultTypeConverter.java index 11b55a86d..29316d98e 100644 --- a/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/DefaultTypeConverter.java +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/DefaultTypeConverter.java @@ -30,6 +30,7 @@ //-------------------------------------------------------------------------- package com.opensymphony.xwork2.conversion.impl; +import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.conversion.TypeConverter; import com.opensymphony.xwork2.ognl.XWorkTypeConverterWrapper; @@ -39,6 +40,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.util.Collections; import java.util.HashMap; +import java.util.Locale; import java.util.Map; /** @@ -49,6 +51,9 @@ import java.util.Map; * @author Drew Davidson (drew@ognl.org) */ public class DefaultTypeConverter implements TypeConverter { + + protected static String MILLISECOND_FORMAT = ".SSS"; + private static final String NULL_STRING = "null"; private final Map primitiveDefaults; @@ -325,4 +330,16 @@ public class DefaultTypeConverter implements TypeConverter { public static String stringValue(Object value) { return stringValue(value, false); } + + protected Locale getLocale(Map context) { + if (context == null) { + return Locale.getDefault(); + } + Locale locale = (Locale) context.get(ActionContext.LOCALE); + if (locale == null) { + locale = Locale.getDefault(); + } + return locale; + } + } diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/NumberConverter.java b/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/NumberConverter.java new file mode 100644 index 000000000..dfa1e0e07 --- /dev/null +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/NumberConverter.java @@ -0,0 +1,116 @@ +package com.opensymphony.xwork2.conversion.impl; + +import com.opensymphony.xwork2.XWorkException; + +import java.lang.reflect.Member; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Map; + +public class NumberConverter extends DefaultTypeConverter { + + public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType) { + if (value instanceof String) { + if (toType == BigDecimal.class) { + return new BigDecimal((String) value); + } else if (toType == BigInteger.class) { + return new BigInteger((String) value); + } else if (toType.isPrimitive()) { + Object convertedValue = super.convertValue(context, value, toType); + String stringValue = (String) value; + if (!isInRange((Number) convertedValue, stringValue, toType)) + throw new XWorkException("Overflow or underflow casting: \"" + stringValue + "\" into class " + convertedValue.getClass().getName()); + + return convertedValue; + } else { + String stringValue = (String) value; + if (!toType.isPrimitive() && (stringValue == null || stringValue.length() == 0)) { + return null; + } + NumberFormat numFormat = NumberFormat.getInstance(getLocale(context)); + ParsePosition parsePos = new ParsePosition(0); + if (isIntegerType(toType)) { + numFormat.setParseIntegerOnly(true); + } + numFormat.setGroupingUsed(true); + Number number = numFormat.parse(stringValue, parsePos); + + if (parsePos.getIndex() != stringValue.length()) { + throw new XWorkException("Unparseable number: \"" + stringValue + "\" at position " + + parsePos.getIndex()); + } else { + if (!isInRange(number, stringValue, toType)) + throw new XWorkException("Overflow or underflow casting: \"" + stringValue + "\" into class " + number.getClass().getName()); + + value = super.convertValue(context, number, toType); + } + } + } else if (value instanceof Object[]) { + Object[] objArray = (Object[]) value; + + if (objArray.length == 1) { + return convertValue(context, null, null, null, objArray[0], toType); + } + } + + // pass it through DefaultTypeConverter + return super.convertValue(context, value, toType); + } + + protected boolean isInRange(Number value, String stringValue, Class toType) { + Number bigValue = null; + Number lowerBound = null; + Number upperBound = null; + + try { + if (double.class == toType || Double.class == toType) { + bigValue = new BigDecimal(stringValue); + // Double.MIN_VALUE is the smallest positive non-zero number + lowerBound = BigDecimal.valueOf(Double.MAX_VALUE).negate(); + upperBound = BigDecimal.valueOf(Double.MAX_VALUE); + } else if (float.class == toType || Float.class == toType) { + bigValue = new BigDecimal(stringValue); + // Float.MIN_VALUE is the smallest positive non-zero number + lowerBound = BigDecimal.valueOf(Float.MAX_VALUE).negate(); + upperBound = BigDecimal.valueOf(Float.MAX_VALUE); + } else if (byte.class == toType || Byte.class == toType) { + bigValue = new BigInteger(stringValue); + lowerBound = BigInteger.valueOf(Byte.MIN_VALUE); + upperBound = BigInteger.valueOf(Byte.MAX_VALUE); + } else if (char.class == toType || Character.class == toType) { + bigValue = new BigInteger(stringValue); + lowerBound = BigInteger.valueOf(Character.MIN_VALUE); + upperBound = BigInteger.valueOf(Character.MAX_VALUE); + } else if (short.class == toType || Short.class == toType) { + bigValue = new BigInteger(stringValue); + lowerBound = BigInteger.valueOf(Short.MIN_VALUE); + upperBound = BigInteger.valueOf(Short.MAX_VALUE); + } else if (int.class == toType || Integer.class == toType) { + bigValue = new BigInteger(stringValue); + lowerBound = BigInteger.valueOf(Integer.MIN_VALUE); + upperBound = BigInteger.valueOf(Integer.MAX_VALUE); + } else if (long.class == toType || Long.class == toType) { + bigValue = new BigInteger(stringValue); + lowerBound = BigInteger.valueOf(Long.MIN_VALUE); + upperBound = BigInteger.valueOf(Long.MAX_VALUE); + } + } catch (NumberFormatException e) { + //shoult it fail here? BigInteger doesnt seem to be so nice parsing numbers as NumberFormat + return true; + } + + return ((Comparable)bigValue).compareTo(lowerBound) >= 0 && ((Comparable)bigValue).compareTo(upperBound) <= 0; + } + + private boolean isIntegerType(Class type) { + if (double.class == type || float.class == type || Double.class == type || Float.class == type + || char.class == type || Character.class == type) { + return false; + } + + return true; + } + +} diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/StringConverter.java b/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/StringConverter.java new file mode 100644 index 000000000..e9c141737 --- /dev/null +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/StringConverter.java @@ -0,0 +1,73 @@ +package com.opensymphony.xwork2.conversion.impl; + +import org.apache.commons.lang3.StringUtils; + +import java.lang.reflect.Member; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +public class StringConverter extends DefaultTypeConverter { + + @Override + public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType) { + String result = null; + + if (value instanceof int[]) { + int[] x = (int[]) value; + List intArray = new ArrayList(x.length); + + for (int aX : x) { + intArray.add(Integer.valueOf(aX)); + } + + result = StringUtils.join(intArray, ", "); + } else if (value instanceof long[]) { + long[] x = (long[]) value; + List longArray = new ArrayList(x.length); + + for (long aX : x) { + longArray.add(Long.valueOf(aX)); + } + + result = StringUtils.join(longArray, ", "); + } else if (value instanceof double[]) { + double[] x = (double[]) value; + List doubleArray = new ArrayList(x.length); + + for (double aX : x) { + doubleArray.add(new Double(aX)); + } + + result = StringUtils.join(doubleArray, ", "); + } else if (value instanceof boolean[]) { + boolean[] x = (boolean[]) value; + List booleanArray = new ArrayList(x.length); + + for (boolean aX : x) { + booleanArray.add(new Boolean(aX)); + } + + result = StringUtils.join(booleanArray, ", "); + } else if (value instanceof Date) { + DateFormat df = null; + if (value instanceof java.sql.Time) { + df = DateFormat.getTimeInstance(DateFormat.MEDIUM, getLocale(context)); + } else if (value instanceof java.sql.Timestamp) { + SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, + DateFormat.MEDIUM, + getLocale(context)); + df = new SimpleDateFormat(dfmt.toPattern() + MILLISECOND_FORMAT); + } else { + df = DateFormat.getDateInstance(DateFormat.SHORT, getLocale(context)); + } + result = df.format(value); + } else if (value instanceof String[]) { + result = StringUtils.join((String[]) value, ", "); + } + return result; + } +} diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkBasicConverter.java b/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkBasicConverter.java index ea5c2f3ff..447816fa8 100644 --- a/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkBasicConverter.java +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkBasicConverter.java @@ -15,36 +15,16 @@ */ package com.opensymphony.xwork2.conversion.impl; -import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ObjectFactory; import com.opensymphony.xwork2.XWorkException; -import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer; import com.opensymphony.xwork2.conversion.TypeConverter; import com.opensymphony.xwork2.inject.Inject; -import com.opensymphony.xwork2.util.XWorkList; -import org.apache.commons.lang3.StringUtils; -import java.lang.reflect.Array; -import java.lang.reflect.Constructor; import java.lang.reflect.Member; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.text.DateFormat; -import java.text.NumberFormat; -import java.text.ParseException; -import java.text.ParsePosition; -import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; import java.util.Map; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; /** @@ -65,7 +45,19 @@ import java.util.TreeSet; * *

Note that with arrays the type conversion will defer to the type of the array elements and try to convert each * item individually. As with any other type conversion, if the conversion can't be performed the standard type - * conversion error reporting is used to indicate a problem occured while processing the type conversion. + * conversion error reporting is used to indicate a problem occurred while processing the type conversion. + *

As from version 2.3.2 you can override default converters by implementting {@link TypeConverter} interface + * and specifying beans in struts.xml as follow: + * <bean type="com.opensymphony.xwork2.conversion.TypeConverter" name="collection" class="com.application.MyCollectionConverter" scope="singleton"/> + *

+ * There are five types of converters defined: + *

    + *
  • collection
  • + *
  • array
  • + *
  • date
  • + *
  • number
  • + *
  • string
  • + *
*

* * @@ -76,23 +68,15 @@ import java.util.TreeSet; */ public class XWorkBasicConverter extends DefaultTypeConverter { - private static String MILLISECOND_FORMAT = ".SSS"; - - private ObjectTypeDeterminer objectTypeDeterminer; private ObjectFactory objectFactory; - @Inject - public void setObjectTypeDeterminer(ObjectTypeDeterminer det) { - this.objectTypeDeterminer = det; - } - @Inject public void setObjectFactory(ObjectFactory fac) { this.objectFactory = fac; } @Override - public Object convertValue(Map context, Object o, Member member, String s, Object value, Class toType) { + public Object convertValue(Map context, Object o, Member member, String propertyName, Object value, Class toType) { Object result = null; if (value == null || toType.isAssignableFrom(value.getClass())) { @@ -117,18 +101,13 @@ public class XWorkBasicConverter extends DefaultTypeConverter { } else if (toType == Boolean.class) { result = doConvertToBoolean(value); } else if (toType.isArray()) { - result = doConvertToArray(context, o, member, s, value, toType); + result = doConvertToArray(context, o, member, propertyName, value, toType); } else if (Date.class.isAssignableFrom(toType)) { result = doConvertToDate(context, value, toType); } else if (Calendar.class.isAssignableFrom(toType)) { - Date dateResult = (Date) doConvertToDate(context, value, Date.class); - if (dateResult != null) { - Calendar calendar = Calendar.getInstance(); - calendar.setTime(dateResult); - result = calendar; - } + result = doConvertToCalendar(context, value); } else if (Collection.class.isAssignableFrom(toType)) { - result = doConvertToCollection(context, o, member, s, value, toType); + result = doConvertToCollection(context, o, member, propertyName, value, toType); } else if (toType == Character.class) { result = doConvertToCharacter(value); } else if (toType == char.class) { @@ -150,7 +129,7 @@ public class XWorkBasicConverter extends DefaultTypeConverter { } // let's try to convert the first element only - result = convertValue(context, o, member, s, value, toType); + result = convertValue(context, o, member, propertyName, value, toType); } else if (!"".equals(value)) { // we've already tried the types we know result = super.convertValue(context, value, toType); } @@ -163,110 +142,43 @@ public class XWorkBasicConverter extends DefaultTypeConverter { return result; } - private Locale getLocale(Map context) { - if (context == null) { - return Locale.getDefault(); - } - - Locale locale = (Locale) context.get(ActionContext.LOCALE); - - if (locale == null) { - locale = Locale.getDefault(); - } - - return locale; - } - - /** - * Creates a Collection of the specified type. - * - * @param fromObject - * @param propertyName - * @param toType the type of Collection to create - * @param memberType the type of object elements in this collection must be - * @param size the initial size of the collection (ignored if 0 or less) - * @return a Collection of the specified type - */ - private Collection createCollection(Object fromObject, String propertyName, Class toType, Class memberType, int size) { -// try { -// Object original = Ognl.getValue(OgnlUtil.compile(propertyName),fromObject); -// if (original instanceof Collection) { -// Collection coll = (Collection) original; -// coll.clear(); -// return coll; -// } -// } catch (Exception e) { -// // fail back to creating a new one -// } - - Collection result; - - if (toType == Set.class) { - if (size > 0) { - result = new HashSet(size); - } else { - result = new HashSet(); - } - } else if (toType == SortedSet.class) { - result = new TreeSet(); - } else { - if (size > 0) { - result = new XWorkList(objectFactory, memberType, size); - } else { - result = new XWorkList(objectFactory, memberType); - } - } - - return result; - } - - private Object doConvertToArray(Map context, Object o, Member member, String s, Object value, Class toType) { + private Object doConvertToCalendar(Map context, Object value) { Object result = null; - Class componentType = toType.getComponentType(); - - if (componentType != null) { - TypeConverter converter = getTypeConverter(context); - - if (value.getClass().isArray()) { - int length = Array.getLength(value); - result = Array.newInstance(componentType, length); - - for (int i = 0; i < length; i++) { - Object valueItem = Array.get(value, i); - Array.set(result, i, converter.convertValue(context, o, member, s, valueItem, componentType)); - } - } else { - result = Array.newInstance(componentType, 1); - Array.set(result, 0, converter.convertValue(context, o, member, s, value, componentType)); - } + Date dateResult = (Date) doConvertToDate(context, value, Date.class); + if (dateResult != null) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(dateResult); + result = calendar; } - return result; } + private Object doConvertToArray(Map context, Object o, Member member, String prop, Object value, Class toType) { + TypeConverter converter = objectFactory.buildConverter("array"); + if (converter == null) { + throw new XWorkException("TypeConverter with name [array] must be registered first!"); + } + return converter.convertValue(context, o, member, prop, value, toType); + } + private Object doConvertToCharacter(Object value) { if (value instanceof String) { String cStr = (String) value; - return (cStr.length() > 0) ? new Character(cStr.charAt(0)) : null; } - return null; } private Object doConvertToBoolean(Object value) { if (value instanceof String) { String bStr = (String) value; - return Boolean.valueOf(bStr); } - return null; } private Class doConvertToClass(Object value) { Class clazz = null; - if (value instanceof String && value != null && ((String) value).length() > 0) { try { clazz = Class.forName((String) value); @@ -274,317 +186,38 @@ public class XWorkBasicConverter extends DefaultTypeConverter { throw new XWorkException(e.getLocalizedMessage(), e); } } - return clazz; } - private Collection doConvertToCollection(Map context, Object o, Member member, String prop, Object value, Class toType) { - Collection result; - Class memberType = String.class; - - if (o != null) { - //memberType = (Class) XWorkConverter.getInstance().getConverter(o.getClass(), XWorkConverter.CONVERSION_COLLECTION_PREFIX + prop); - memberType = objectTypeDeterminer.getElementClass(o.getClass(), prop, null); - - if (memberType == null) { - memberType = String.class; - } + private Object doConvertToCollection(Map context, Object o, Member member, String prop, Object value, Class toType) { + TypeConverter converter = objectFactory.buildConverter("collection"); + if (converter == null) { + throw new XWorkException("TypeConverter with name [collection] must be registered first!"); } - - if (toType.isAssignableFrom(value.getClass())) { - // no need to do anything - result = (Collection) value; - } else if (value.getClass().isArray()) { - Object[] objArray = (Object[]) value; - TypeConverter converter = getTypeConverter(context); - result = createCollection(o, prop, toType, memberType, objArray.length); - - for (Object anObjArray : objArray) { - result.add(converter.convertValue(context, o, member, prop, anObjArray, memberType)); - } - } else if (Collection.class.isAssignableFrom(value.getClass())) { - Collection col = (Collection) value; - TypeConverter converter = getTypeConverter(context); - result = createCollection(o, prop, toType, memberType, col.size()); - - for (Object aCol : col) { - result.add(converter.convertValue(context, o, member, prop, aCol, memberType)); - } - } else { - result = createCollection(o, prop, toType, memberType, -1); - result.add(value); - } - - return result; + return converter.convertValue(context, o, member, prop, value, toType); } private Object doConvertToDate(Map context, Object value, Class toType) { - Date result = null; - - if (value instanceof String && value != null && ((String) value).length() > 0) { - String sa = (String) value; - Locale locale = getLocale(context); - - DateFormat df = null; - if (java.sql.Time.class == toType) { - df = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale); - } else if (java.sql.Timestamp.class == toType) { - Date check = null; - SimpleDateFormat dtfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, - DateFormat.MEDIUM, - locale); - SimpleDateFormat fullfmt = new SimpleDateFormat(dtfmt.toPattern() + MILLISECOND_FORMAT, - locale); - - SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, - locale); - - SimpleDateFormat[] fmts = {fullfmt, dtfmt, dfmt}; - for (SimpleDateFormat fmt : fmts) { - try { - check = fmt.parse(sa); - df = fmt; - if (check != null) { - break; - } - } catch (ParseException ignore) { - } - } - } else if (java.util.Date.class == toType) { - Date check = null; - DateFormat[] dfs = getDateFormats(locale); - for (DateFormat df1 : dfs) { - try { - check = df1.parse(sa); - df = df1; - if (check != null) { - break; - } - } - catch (ParseException ignore) { - } - } - } - //final fallback for dates without time - if (df == null) { - df = DateFormat.getDateInstance(DateFormat.SHORT, locale); - } - try { - df.setLenient(false); // let's use strict parsing (XW-341) - result = df.parse(sa); - if (!(Date.class == toType)) { - try { - Constructor constructor = toType.getConstructor(new Class[]{long.class}); - return constructor.newInstance(new Object[]{Long.valueOf(result.getTime())}); - } catch (Exception e) { - throw new XWorkException("Couldn't create class " + toType + " using default (long) constructor", e); - } - } - } catch (ParseException e) { - throw new XWorkException("Could not parse date", e); - } - } else if (Date.class.isAssignableFrom(value.getClass())) { - result = (Date) value; + TypeConverter converter = objectFactory.buildConverter("date"); + if (converter == null) { + throw new XWorkException("TypeConverter with name [date] must be registered first!"); } - return result; - } - - private DateFormat[] getDateFormats(Locale locale) { - DateFormat dt1 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG, locale); - DateFormat dt2 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale); - DateFormat dt3 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale); - - DateFormat d1 = DateFormat.getDateInstance(DateFormat.SHORT, locale); - DateFormat d2 = DateFormat.getDateInstance(DateFormat.MEDIUM, locale); - DateFormat d3 = DateFormat.getDateInstance(DateFormat.LONG, locale); - - DateFormat rfc3399 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); - - DateFormat[] dfs = {dt1, dt2, dt3, rfc3399, d1, d2, d3}; //added RFC 3339 date format (XW-473) - return dfs; + return converter.convertValue(context, null, null, null, value, toType); } private Object doConvertToNumber(Map context, Object value, Class toType) { - if (value instanceof String) { - if (toType == BigDecimal.class) { - return new BigDecimal((String) value); - } else if (toType == BigInteger.class) { - return new BigInteger((String) value); - } else if (toType.isPrimitive()) { - Object convertedValue = super.convertValue(context, value, toType); - String stringValue = (String) value; - if (!isInRange((Number)convertedValue, stringValue, toType)) - throw new XWorkException("Overflow or underflow casting: \"" + stringValue + "\" into class " + convertedValue.getClass().getName()); - - return convertedValue; - } else { - String stringValue = (String) value; - if (!toType.isPrimitive() && (stringValue == null || stringValue.length() == 0)) { - return null; - } - NumberFormat numFormat = NumberFormat.getInstance(getLocale(context)); - ParsePosition parsePos = new ParsePosition(0); - if (isIntegerType(toType)) { - numFormat.setParseIntegerOnly(true); - } - numFormat.setGroupingUsed(true); - Number number = numFormat.parse(stringValue, parsePos); - - if (parsePos.getIndex() != stringValue.length()) { - throw new XWorkException("Unparseable number: \"" + stringValue + "\" at position " - + parsePos.getIndex()); - } else { - if (!isInRange(number, stringValue, toType)) - throw new XWorkException("Overflow or underflow casting: \"" + stringValue + "\" into class " + number.getClass().getName()); - - value = super.convertValue(context, number, toType); - } - } - } else if (value instanceof Object[]) { - Object[] objArray = (Object[]) value; - - if (objArray.length == 1) { - return doConvertToNumber(context, objArray[0], toType); - } + TypeConverter converter = objectFactory.buildConverter("number"); + if (converter == null) { + throw new XWorkException("TypeConverter with name [number] must be registered first!"); } - - // pass it through DefaultTypeConverter - return super.convertValue(context, value, toType); + return converter.convertValue(context, null, null, null, value, toType); } - protected boolean isInRange(Number value, String stringValue, Class toType) { - Number bigValue = null; - Number lowerBound = null; - Number upperBound = null; - - try { - if (double.class == toType || Double.class == toType) { - bigValue = new BigDecimal(stringValue); - // Double.MIN_VALUE is the smallest positive non-zero number - lowerBound = BigDecimal.valueOf(Double.MAX_VALUE).negate(); - upperBound = BigDecimal.valueOf(Double.MAX_VALUE); - } else if (float.class == toType || Float.class == toType) { - bigValue = new BigDecimal(stringValue); - // Float.MIN_VALUE is the smallest positive non-zero number - lowerBound = BigDecimal.valueOf(Float.MAX_VALUE).negate(); - upperBound = BigDecimal.valueOf(Float.MAX_VALUE); - } else if (byte.class == toType || Byte.class == toType) { - bigValue = new BigInteger(stringValue); - lowerBound = BigInteger.valueOf(Byte.MIN_VALUE); - upperBound = BigInteger.valueOf(Byte.MAX_VALUE); - } else if (char.class == toType || Character.class == toType) { - bigValue = new BigInteger(stringValue); - lowerBound = BigInteger.valueOf(Character.MIN_VALUE); - upperBound = BigInteger.valueOf(Character.MAX_VALUE); - } else if (short.class == toType || Short.class == toType) { - bigValue = new BigInteger(stringValue); - lowerBound = BigInteger.valueOf(Short.MIN_VALUE); - upperBound = BigInteger.valueOf(Short.MAX_VALUE); - } else if (int.class == toType || Integer.class == toType) { - bigValue = new BigInteger(stringValue); - lowerBound = BigInteger.valueOf(Integer.MIN_VALUE); - upperBound = BigInteger.valueOf(Integer.MAX_VALUE); - } else if (long.class == toType || Long.class == toType) { - bigValue = new BigInteger(stringValue); - lowerBound = BigInteger.valueOf(Long.MIN_VALUE); - upperBound = BigInteger.valueOf(Long.MAX_VALUE); - } - } catch (NumberFormatException e) { - //shoult it fail here? BigInteger doesnt seem to be so nice parsing numbers as NumberFormat - return true; + private Object doConvertToString(Map context, Object value) { + TypeConverter converter = objectFactory.buildConverter("string"); + if (converter == null) { + throw new XWorkException("TypeConverter with name [string] must be registered first!"); } - - return ((Comparable)bigValue).compareTo(lowerBound) >= 0 && ((Comparable)bigValue).compareTo(upperBound) <= 0; - } - - protected boolean isIntegerType(Class type) { - if (double.class == type || float.class == type || Double.class == type || Float.class == type - || char.class == type || Character.class == type) { - return false; - } - - return true; - } - - /** - * Converts the input as a number using java's number formatter to a string output. - */ - private String doConvertFromNumberToString(Map context, Object value, Class toType) { - // XW-409: If the input is a Number we should format it to a string using the choosen locale and use java's numberformatter - if (Number.class.isAssignableFrom(toType)) { - NumberFormat numFormat = NumberFormat.getInstance(getLocale(context)); - if (isIntegerType(toType)) { - numFormat.setParseIntegerOnly(true); - } - numFormat.setGroupingUsed(true); - numFormat.setMaximumFractionDigits(99); // to be sure we include all digits after decimal seperator, otherwise some of the fractions can be chopped - - String number = numFormat.format(value); - if (number != null) { - return number; - } - } - - return null; // no number - } - - - private String doConvertToString(Map context, Object value) { - String result = null; - - if (value instanceof int[]) { - int[] x = (int[]) value; - List intArray = new ArrayList(x.length); - - for (int aX : x) { - intArray.add(Integer.valueOf(aX)); - } - - result = StringUtils.join(intArray, ", "); - } else if (value instanceof long[]) { - long[] x = (long[]) value; - List longArray = new ArrayList(x.length); - - for (long aX : x) { - longArray.add(Long.valueOf(aX)); - } - - result = StringUtils.join(longArray, ", "); - } else if (value instanceof double[]) { - double[] x = (double[]) value; - List doubleArray = new ArrayList(x.length); - - for (double aX : x) { - doubleArray.add(new Double(aX)); - } - - result = StringUtils.join(doubleArray, ", "); - } else if (value instanceof boolean[]) { - boolean[] x = (boolean[]) value; - List booleanArray = new ArrayList(x.length); - - for (boolean aX : x) { - booleanArray.add(new Boolean(aX)); - } - - result = StringUtils.join(booleanArray, ", "); - } else if (value instanceof Date) { - DateFormat df = null; - if (value instanceof java.sql.Time) { - df = DateFormat.getTimeInstance(DateFormat.MEDIUM, getLocale(context)); - } else if (value instanceof java.sql.Timestamp) { - SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, - DateFormat.MEDIUM, - getLocale(context)); - df = new SimpleDateFormat(dfmt.toPattern() + MILLISECOND_FORMAT); - } else { - df = DateFormat.getDateInstance(DateFormat.SHORT, getLocale(context)); - } - result = df.format(value); - } else if (value instanceof String[]) { - result = StringUtils.join((String[]) value, ", "); - } - - return result; + return converter.convertValue(context, null, null, null, value, null); } } diff --git a/xwork-core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkBasicConverterTest.java b/xwork-core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkBasicConverterTest.java index 77e9884e0..95fb13f43 100644 --- a/xwork-core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkBasicConverterTest.java +++ b/xwork-core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkBasicConverterTest.java @@ -17,12 +17,16 @@ package com.opensymphony.xwork2.conversion.impl; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.XWorkException; +import com.opensymphony.xwork2.XWorkTestCase; import com.opensymphony.xwork2.test.annotations.Person; -import junit.framework.TestCase; -import java.text.DateFormat; -import java.util.*; import java.lang.reflect.Member; +import java.text.DateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; /** * Test case for XWorkBasicConverter @@ -30,7 +34,9 @@ import java.lang.reflect.Member; * @author tm_jee * @version $Date$ $Id$ */ -public class XWorkBasicConverterTest extends TestCase { +public class XWorkBasicConverterTest extends XWorkTestCase { + + private XWorkBasicConverter basicConverter; // TODO: test for every possible conversion // take into account of empty string @@ -38,14 +44,12 @@ public class XWorkBasicConverterTest extends TestCase { // object -> return null when empty string is passed public void testDateConversionWithEmptyValue() { - XWorkBasicConverter basicConverter = new XWorkBasicConverter(); Object convertedObject = basicConverter.convertValue(new HashMap(), null, null, null, "", Date.class); // we must not get XWorkException as that will caused a conversion error assertNull(convertedObject); } public void testDateConversionWithInvalidValue() throws Exception { - XWorkBasicConverter basicConverter = new XWorkBasicConverter(); try { Object convertedObject = basicConverter.convertValue(new HashMap(), null, null, null, "asdsd", Date.class); fail("XWorkException expected - conversion error occurred"); @@ -55,7 +59,6 @@ public class XWorkBasicConverterTest extends TestCase { } public void testDateWithLocalePoland() throws Exception { - XWorkBasicConverter basicConverter = new XWorkBasicConverter(); Map map = new HashMap(); Locale locale = new Locale("pl", "PL"); @@ -70,7 +73,6 @@ public class XWorkBasicConverterTest extends TestCase { } public void testDateWithLocaleFrance() throws Exception { - XWorkBasicConverter basicConverter = new XWorkBasicConverter(); Map map = new HashMap(); Locale locale = new Locale("fr", "FR"); @@ -85,7 +87,6 @@ public class XWorkBasicConverterTest extends TestCase { } public void testDateWithLocaleUK() throws Exception { - XWorkBasicConverter basicConverter = new XWorkBasicConverter(); Map map = new HashMap(); Locale locale = new Locale("en", "US"); @@ -117,7 +118,6 @@ public class XWorkBasicConverterTest extends TestCase { } public void testEmptyArrayConversion() throws Exception { - XWorkBasicConverter basicConverter = new XWorkBasicConverter(); Object convertedObject = basicConverter.convertValue(new HashMap(), null, null, null, new Object[]{}, Object[].class); // we must not get XWorkException as that will caused a conversion error assertEquals(Object[].class, convertedObject.getClass()); @@ -126,7 +126,6 @@ public class XWorkBasicConverterTest extends TestCase { } public void testNullArrayConversion() throws Exception { - XWorkBasicConverter basicConverter = new XWorkBasicConverter(); Object convertedObject = basicConverter.convertValue(new HashMap(), null, null, null, null, Object[].class); // we must not get XWorkException as that will caused a conversion error assertNull(convertedObject); @@ -165,10 +164,10 @@ public class XWorkBasicConverterTest extends TestCase { */ public void testDoubleValues() { - XWorkBasicConverter basicConverter = new XWorkBasicConverter(); + NumberConverter numberConverter = new NumberConverter(); - assertTrue(basicConverter.isInRange(-1.2, "-1.2", Double.class)); - assertTrue(basicConverter.isInRange(1.5, "1.5", Double.class)); + assertTrue(numberConverter.isInRange(-1.2, "-1.2", Double.class)); + assertTrue(numberConverter.isInRange(1.5, "1.5", Double.class)); Object value = basicConverter.convertValue("-1.3", double.class); assertNotNull(value); @@ -196,10 +195,10 @@ public class XWorkBasicConverterTest extends TestCase { } public void testFloatValues() { - XWorkBasicConverter basicConverter = new XWorkBasicConverter(); + NumberConverter numberConverter = new NumberConverter(); - assertTrue(basicConverter.isInRange(-1.65, "-1.65", Float.class)); - assertTrue(basicConverter.isInRange(1.9876, "1.9876", float.class)); + assertTrue(numberConverter.isInRange(-1.65, "-1.65", Float.class)); + assertTrue(numberConverter.isInRange(1.9876, "1.9876", float.class)); Float value = (Float) basicConverter.convertValue("-1.444401", Float.class); assertNotNull(value); @@ -211,14 +210,12 @@ public class XWorkBasicConverterTest extends TestCase { } public void testNegativeFloatValue() throws Exception { - XWorkBasicConverter basicConverter = new XWorkBasicConverter(); Object convertedObject = basicConverter.convertValue("-94.1231233", Float.class); assertTrue(convertedObject instanceof Float); assertEquals(-94.1231233f, ((Float) convertedObject).floatValue(), 0.0001); } public void testPositiveFloatValue() throws Exception { - XWorkBasicConverter basicConverter = new XWorkBasicConverter(); Object convertedObject = basicConverter.convertValue("94.1231233", Float.class); assertTrue(convertedObject instanceof Float); assertEquals(94.1231233f, ((Float) convertedObject).floatValue(), 0.0001); @@ -226,21 +223,18 @@ public class XWorkBasicConverterTest extends TestCase { public void testNegativeDoubleValue() throws Exception { - XWorkBasicConverter basicConverter = new XWorkBasicConverter(); Object convertedObject = basicConverter.convertValue("-94.1231233", Double.class); assertTrue(convertedObject instanceof Double); assertEquals(-94.1231233d, ((Double) convertedObject).doubleValue(), 0.0001); } public void testPositiveDoubleValue() throws Exception { - XWorkBasicConverter basicConverter = new XWorkBasicConverter(); Object convertedObject = basicConverter.convertValue("94.1231233", Double.class); assertTrue(convertedObject instanceof Double); assertEquals(94.1231233d, ((Double) convertedObject).doubleValue(), 0.0001); } public void testNestedEnumValue() throws Exception { - XWorkBasicConverter basicConverter = new XWorkBasicConverter(); Object convertedObject = basicConverter.convertValue(ParentClass.NestedEnum.TEST.name(), ParentClass.NestedEnum.class); assertTrue(convertedObject instanceof ParentClass.NestedEnum); assertEquals(ParentClass.NestedEnum.TEST, convertedObject); @@ -248,14 +242,25 @@ public class XWorkBasicConverterTest extends TestCase { public void testConvert() { - XWorkBasicConverter converter = new XWorkBasicConverter(); Map context = new HashMap(); Person o = new Person(); Member member = null; String s = "names"; Object value = new Person[0]; Class toType = String.class; - converter.convertValue(context, value, member, s, value, toType); - } + basicConverter.convertValue(context, value, member, s, value, toType); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + basicConverter = container.getInstance(XWorkBasicConverter.class); + } + + @Override + protected void tearDown() throws Exception { + ActionContext.setContext(null); + } + } diff --git a/xwork-core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkConverterTest.java b/xwork-core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkConverterTest.java index ba2fdddcb..bc0d93238 100644 --- a/xwork-core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkConverterTest.java +++ b/xwork-core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkConverterTest.java @@ -15,7 +15,11 @@ */ package com.opensymphony.xwork2.conversion.impl; -import com.opensymphony.xwork2.*; +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.ModelDrivenAction; +import com.opensymphony.xwork2.SimpleAction; +import com.opensymphony.xwork2.TestBean; +import com.opensymphony.xwork2.XWorkTestCase; import com.opensymphony.xwork2.ognl.OgnlValueStack; import com.opensymphony.xwork2.test.ModelDrivenAction2; import com.opensymphony.xwork2.test.User; @@ -35,7 +39,16 @@ import java.sql.Timestamp; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; /** @@ -665,6 +678,17 @@ public class XWorkConverterTest extends XWorkTestCase { assertEquals(321, cat.getFoo().getNumber()); } + public void testCollectionConversion() throws Exception { + // given + String[] col1 = new String[]{"1", "2", "ble", "3"}; + + // when + Object converted = converter.convertValue(context, new ListAction(), null, "ints", col1, List.class); + + // then + assertEquals(converted, Arrays.asList(1, 2, 3)); + } + public static class Foo1 { public Bar1 getBar() { return new Bar1Impl(); @@ -690,3 +714,17 @@ public class XWorkConverterTest extends XWorkTestCase { } } + +class ListAction { + + private List ints = new ArrayList(); + + public List getInts() { + return ints; + } + + public void setInts(List ints) { + this.ints = ints; + } + +} \ No newline at end of file