Skip to content

Commit 266d78d

Browse files
committed
WW-3650 Supports double conversion for different locale
1 parent f874f9c commit 266d78d

File tree

3 files changed

+94
-26
lines changed

3 files changed

+94
-26
lines changed

core/src/main/java/com/opensymphony/xwork2/conversion/impl/NumberConverter.java

+55-14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.opensymphony.xwork2.conversion.impl;
22

33
import com.opensymphony.xwork2.XWorkException;
4+
import com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator;
45
import org.apache.logging.log4j.LogManager;
56
import org.apache.logging.log4j.Logger;
67
import org.apache.logging.log4j.message.ParameterizedMessage;
@@ -28,6 +29,8 @@ public Object convertValue(Map<String, Object> context, Object target, Member me
2829
return convertToBigDecimal(context, value);
2930
} else if (toType == BigInteger.class) {
3031
return new BigInteger((String) value);
32+
} else if (toType == Double.class || toType == double.class) {
33+
return convertToDouble(context, value);
3134
} else if (toType.isPrimitive()) {
3235
Object convertedValue = super.convertValue(context, value, toType);
3336
String stringValue = (String) value;
@@ -74,27 +77,65 @@ protected Object convertToBigDecimal(Map<String, Object> context, Object value)
7477
Locale locale = getLocale(context);
7578
String strValue = String.valueOf(value);
7679

77-
NumberFormat format = NumberFormat.getNumberInstance(locale);
78-
format.setGroupingUsed(true);
80+
NumberFormat format = getNumberFormat(locale);
7981
if (format instanceof DecimalFormat) {
8082
((DecimalFormat) format).setParseBigDecimal(true);
8183
char separator = ((DecimalFormat) format).getDecimalFormatSymbols().getGroupingSeparator();
84+
strValue = normalize(strValue, separator);
85+
}
8286

83-
// this is a hack as \160 isn't the same as " " (an empty space)
84-
if (separator == 160) {
85-
strValue = strValue.replaceAll(" ", "");
86-
} else {
87-
strValue = strValue.replaceAll(String.valueOf(separator), "");
88-
}
87+
LOG.debug("Trying to convert a value {} with locale {} to BigDecimal", strValue, locale);
88+
ParsePosition parsePosition = new ParsePosition(0);
89+
Number number = format.parse(strValue, parsePosition);
90+
91+
if (parsePosition.getIndex() != strValue.length()) {
92+
throw new XWorkException("Unparseable number: \"" + strValue + "\" at position " + parsePosition.getIndex());
8993
}
9094

91-
try {
92-
LOG.info("Trying parse value {} with locale {}", strValue, locale);
93-
return format.parse(strValue);
94-
} catch (ParseException e) {
95-
LOG.warn(new ParameterizedMessage("Cannot convert value {} to BigDecimal, trying with default converter", value, e));
96-
return super.convertValue(context, value, BigDecimal.class);
95+
return number;
96+
}
97+
98+
protected Object convertToDouble(Map<String, Object> context, Object value) {
99+
Locale locale = getLocale(context);
100+
String strValue = String.valueOf(value);
101+
102+
NumberFormat format = getNumberFormat(locale);
103+
if (format instanceof DecimalFormat) {
104+
char separator = ((DecimalFormat) format).getDecimalFormatSymbols().getGroupingSeparator();
105+
strValue = normalize(strValue, separator);
106+
}
107+
108+
LOG.debug("Trying to convert a value {} with locale {} to Double", strValue, locale);
109+
ParsePosition parsePosition = new ParsePosition(0);
110+
Number number = format.parse(strValue, parsePosition);
111+
112+
if (parsePosition.getIndex() != strValue.length()) {
113+
throw new XWorkException("Unparseable number: \"" + strValue + "\" at position " + parsePosition.getIndex());
114+
}
115+
116+
if (!isInRange(number, strValue, Double.class)) {
117+
throw new XWorkException("Overflow or underflow converting: \"" + strValue + "\" into class " + number.getClass().getName());
118+
}
119+
120+
if (number != null) {
121+
return number.doubleValue();
122+
}
123+
124+
return null;
125+
}
126+
127+
protected NumberFormat getNumberFormat(Locale locale) {
128+
NumberFormat format = NumberFormat.getNumberInstance(locale);
129+
format.setGroupingUsed(true);
130+
return format;
131+
}
132+
133+
protected String normalize(String strValue, char separator) {
134+
// this is a hack as \160 isn't the same as " " (an empty space)
135+
if (separator == 160) {
136+
strValue = strValue.replaceAll(" ", String.valueOf(separator));
97137
}
138+
return strValue;
98139
}
99140

100141
protected boolean isInRange(Number value, String stringValue, Class toType) {

core/src/test/java/com/opensymphony/xwork2/conversion/impl/NumberConverterTest.java

+27
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,31 @@ public void testStringToBigDecimalConversionWithCommasEN() throws Exception {
8181
assertEquals(BigDecimal.valueOf(100234.4), value);
8282
}
8383

84+
public void testStringToDoubleConversionPL() throws Exception {
85+
// given
86+
NumberConverter converter = new NumberConverter();
87+
Map<String, Object> context = new HashMap<>();
88+
context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
89+
90+
// when
91+
Object value = converter.convertValue(context, null, null, null, "1234,4567", Double.class);
92+
93+
// then
94+
assertEquals(1234.4567, value);
95+
}
96+
97+
public void testStringToDoubleConversionWithDotsPL() throws Exception {
98+
// given
99+
NumberConverter converter = new NumberConverter();
100+
Map<String, Object> context = new HashMap<>();
101+
context.put(ActionContext.LOCALE, new Locale("pl", "PL"));
102+
103+
// when
104+
Object value = converter.convertValue(context, null, null, null, "1 234,4", Double.class);
105+
106+
// then
107+
assertEquals(1234.4, value);
108+
}
109+
110+
84111
}

core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkConverterTest.java

+12-12
Original file line numberDiff line numberDiff line change
@@ -519,30 +519,30 @@ public void testStringToInteger() {
519519
}
520520

521521
public void testStringToPrimitiveDouble() {
522-
assertEquals(new Double(123), converter.convertValue(context, null, null, null, "123", double.class));
522+
assertEquals(123d, converter.convertValue(context, null, null, null, "123", double.class));
523523
context.put(ActionContext.LOCALE, Locale.US);
524-
assertEquals(new Double(123.12), converter.convertValue(context, null, null, null, "123.12", double.class));
524+
assertEquals(123.12, converter.convertValue(context, null, null, null, "123.12", double.class));
525525
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "123aa", double.class));
526526
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "aa123", double.class));
527-
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1,234", double.class));
528-
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1,234.12", double.class));
529-
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1,23", double.class));
530-
assertEquals(new Double(1.234), converter.convertValue(context, null, null, null, "1.234", double.class));
527+
assertEquals(1234d, converter.convertValue(context, null, null, null, "1,234", double.class));
528+
assertEquals(1234.12, converter.convertValue(context, null, null, null, "1,234.12", double.class));
529+
assertEquals(123d, converter.convertValue(context, null, null, null, "1,23", double.class));
530+
assertEquals(1.234, converter.convertValue(context, null, null, null, "1.234", double.class));
531531
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1.234,12", double.class));
532532

533533
context.put(ActionContext.LOCALE, Locale.GERMANY);
534-
assertEquals(new Double(123.12), converter.convertValue(context, null, null, null, "123.12", double.class));
534+
assertEquals(12312d, converter.convertValue(context, null, null, null, "123.12", double.class));
535535
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "123aa", double.class));
536536
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "aa123", double.class));
537-
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1,234", double.class));
537+
assertEquals(1.234, converter.convertValue(context, null, null, null, "1,234", double.class));
538538
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1,234.12", double.class));
539-
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1,23", double.class));
540-
assertEquals(new Double(1.234), converter.convertValue(context, null, null, null, "1.234", double.class));
541-
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "1.234,12", double.class));
539+
assertEquals(1.23, converter.convertValue(context, null, null, null, "1,23", double.class));
540+
assertEquals(1234d, converter.convertValue(context, null, null, null, "1.234", double.class));
541+
assertEquals(1234.12, converter.convertValue(context, null, null, null, "1.234,12", double.class));
542542
}
543543

544544
public void testStringToDouble() {
545-
assertEquals(new Double(123), converter.convertValue(context, null, null, null, "123", Double.class));
545+
assertEquals(123d, converter.convertValue(context, null, null, null, "123", Double.class));
546546
context.put(ActionContext.LOCALE, Locale.US);
547547
assertEquals(new Double(123.12), converter.convertValue(context, null, null, null, "123.12", Double.class));
548548
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "123aa", Double.class));

0 commit comments

Comments
 (0)