mirror of
https://github.com/apache/struts.git
synced 2026-08-07 15:46:57 +00:00
WW-3762 - extracts one huge converter into smaller classes and implements better conversion mechanism for collections
git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@1297347 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -63,6 +63,12 @@
|
||||
<bean type="com.opensymphony.xwork2.conversion.impl.XWorkConverter" name="xwork1" class="com.opensymphony.xwork2.conversion.impl.XWorkConverter" />
|
||||
<bean class="com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter" />
|
||||
|
||||
<bean type="com.opensymphony.xwork2.conversion.TypeConverter" name="collection" class="com.opensymphony.xwork2.conversion.impl.CollectionConverter" scope="singleton"/>
|
||||
<bean type="com.opensymphony.xwork2.conversion.TypeConverter" name="array" class="com.opensymphony.xwork2.conversion.impl.ArrayConverter" scope="singleton"/>
|
||||
<bean type="com.opensymphony.xwork2.conversion.TypeConverter" name="date" class="com.opensymphony.xwork2.conversion.impl.DateConverter" scope="singleton"/>
|
||||
<bean type="com.opensymphony.xwork2.conversion.TypeConverter" name="number" class="com.opensymphony.xwork2.conversion.impl.NumberConverter" scope="singleton"/>
|
||||
<bean type="com.opensymphony.xwork2.conversion.TypeConverter" name="string" class="com.opensymphony.xwork2.conversion.impl.StringConverter" scope="singleton"/>
|
||||
|
||||
<bean type="com.opensymphony.xwork2.TextProvider" name="xwork1" class="com.opensymphony.xwork2.TextProviderSupport" scope="default" />
|
||||
<bean type="com.opensymphony.xwork2.TextProvider" name="struts" class="com.opensymphony.xwork2.TextProviderSupport" scope="default" />
|
||||
|
||||
|
||||
@@ -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<? extends TypeConverter> 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<? extends TypeConverter> 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 {
|
||||
|
||||
}
|
||||
|
||||
+35
-4
@@ -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);
|
||||
|
||||
+12
-1
@@ -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());
|
||||
|
||||
@@ -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<String, Object> 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;
|
||||
}
|
||||
|
||||
}
|
||||
+101
@@ -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<String, Object> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String, Object> 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};
|
||||
}
|
||||
|
||||
}
|
||||
+17
@@ -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<Class, Object> primitiveDefaults;
|
||||
@@ -325,4 +330,16 @@ public class DefaultTypeConverter implements TypeConverter {
|
||||
public static String stringValue(Object value) {
|
||||
return stringValue(value, false);
|
||||
}
|
||||
|
||||
protected Locale getLocale(Map<String, Object> context) {
|
||||
if (context == null) {
|
||||
return Locale.getDefault();
|
||||
}
|
||||
Locale locale = (Locale) context.get(ActionContext.LOCALE);
|
||||
if (locale == null) {
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
return locale;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+116
@@ -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<String, Object> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType) {
|
||||
String result = null;
|
||||
|
||||
if (value instanceof int[]) {
|
||||
int[] x = (int[]) value;
|
||||
List<Integer> intArray = new ArrayList<Integer>(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<Long> longArray = new ArrayList<Long>(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<Double> doubleArray = new ArrayList<Double>(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<Boolean> booleanArray = new ArrayList<Boolean>(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;
|
||||
}
|
||||
}
|
||||
+50
-417
@@ -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;
|
||||
* </ul>
|
||||
* <p/> 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.
|
||||
* <p/> 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"/>
|
||||
* <p/>
|
||||
* There are five types of converters defined:
|
||||
* <ul>
|
||||
* <li>collection</li>
|
||||
* <li>array</li>
|
||||
* <li>date</li>
|
||||
* <li>number</li>
|
||||
* <li>string</li>
|
||||
* </ul>
|
||||
* <p/>
|
||||
* <!-- END SNIPPET: javadoc -->
|
||||
*
|
||||
@@ -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<String, Object> context, Object o, Member member, String s, Object value, Class toType) {
|
||||
public Object convertValue(Map<String, Object> 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<String, Object> 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<String, Object> context, Object o, Member member, String s, Object value, Class toType) {
|
||||
private Object doConvertToCalendar(Map<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> context, Object value) {
|
||||
String result = null;
|
||||
|
||||
if (value instanceof int[]) {
|
||||
int[] x = (int[]) value;
|
||||
List<Integer> intArray = new ArrayList<Integer>(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<Long> longArray = new ArrayList<Long>(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<Double> doubleArray = new ArrayList<Double>(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<Boolean> booleanArray = new ArrayList<Boolean>(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);
|
||||
}
|
||||
}
|
||||
|
||||
+30
-25
@@ -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<String, Object>(), 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<String, Object>(), 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<String, Object> map = new HashMap<String, Object>();
|
||||
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<String, Object> map = new HashMap<String, Object>();
|
||||
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<String, Object> map = new HashMap<String, Object>();
|
||||
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<String, Object>(), 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<String, Object>(), 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+40
-2
@@ -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<Integer> ints = new ArrayList<Integer>();
|
||||
|
||||
public List<Integer> getInts() {
|
||||
return ints;
|
||||
}
|
||||
|
||||
public void setInts(List<Integer> ints) {
|
||||
this.ints = ints;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user