Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
Expand Down Expand Up @@ -83,6 +84,8 @@ public Object convertValue(Map<String, Object> context, Object o, Member member,
return value;
}

Object input = trimSurroundingWhitespace(value, toType);

if (toType == String.class) {
/* the code below has been disabled as it causes sideffects in Struts2 (XW-512)
// if input (value) is a number then use special conversion method (XW-490)
Expand All @@ -94,54 +97,54 @@ public Object convertValue(Map<String, Object> context, Object o, Member member,
}
}*/
// okay use default string conversion
result = doConvertToString(context, value);
result = doConvertToString(context, input);
} else if (toType == boolean.class) {
result = doConvertToBoolean(value);
result = doConvertToBoolean(input);
} else if (toType == Boolean.class) {
result = doConvertToBoolean(value);
result = "".equals(input) ? null : doConvertToBoolean(input);
} else if (toType.isArray()) {
result = doConvertToArray(context, o, member, propertyName, value, toType);
result = doConvertToArray(context, o, member, propertyName, input, toType);
} else if (Date.class.isAssignableFrom(toType)) {
result = doConvertToDate(context, value, toType);
result = doConvertToDate(context, input, toType);
} else if (LocalDate.class.isAssignableFrom(toType)) {
result = doConvertToDate(context, value, toType);
result = doConvertToDate(context, input, toType);
} else if (LocalDateTime.class.isAssignableFrom(toType)) {
result = doConvertToDate(context, value, toType);
result = doConvertToDate(context, input, toType);
} else if (LocalTime.class.isAssignableFrom(toType)) {
result = doConvertToDate(context, value, toType);
result = doConvertToDate(context, input, toType);
} else if (OffsetDateTime.class.isAssignableFrom(toType)) {
result = doConvertToDate(context, value, toType);
result = doConvertToDate(context, input, toType);
} else if (Calendar.class.isAssignableFrom(toType)) {
result = doConvertToCalendar(context, value);
result = doConvertToCalendar(context, input);
} else if (Collection.class.isAssignableFrom(toType)) {
result = doConvertToCollection(context, o, member, propertyName, value, toType);
result = doConvertToCollection(context, o, member, propertyName, input, toType);
} else if (toType == Character.class) {
result = doConvertToCharacter(value);
result = doConvertToCharacter(input);
} else if (toType == char.class) {
result = doConvertToCharacter(value);
result = doConvertToCharacter(input);
} else if (Number.class.isAssignableFrom(toType) || toType.isPrimitive()) {
result = doConvertToNumber(context, value, toType);
result = doConvertToNumber(context, input, toType);
} else if (toType == Class.class) {
result = doConvertToClass(value);
result = doConvertToClass(input);
}

if (result == null) {
if (value instanceof Object[] array) {
if (input instanceof Object[] array) {

if (array.length >= 1) {
value = array[0];
input = array[0];
} else {
value = null;
input = null;
}

// let's try to convert the first element only
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);
result = convertValue(context, o, member, propertyName, input, toType);
} else if (!"".equals(input)) { // we've already tried the types we know
result = super.convertValue(context, input, toType);
}

if (result == null && value != null && !"".equals(value)) {
throw new TypeConversionException("Cannot create type " + toType + " from value " + value);
if (result == null && input != null && !"".equals(input)) {
throw new TypeConversionException("Cannot create type " + toType + " from value " + input);
}
}

Expand All @@ -159,9 +162,24 @@ private Object doConvertToCalendar(Map<String, Object> context, Object value) {
return result;
}

private Object trimSurroundingWhitespace(Object value, Class toType) {
if (toType.isArray() || Collection.class.isAssignableFrom(toType) || toType == Character.class || toType == char.class) {
return value;
}
if (value instanceof String str) {
return str.trim();
}
if (value instanceof String[] values) {
return Arrays.stream(values).map(item -> item == null ? null : item.trim()).toArray(String[]::new);
}
return value;
}

private Object doConvertToCharacter(Object value) {
if (value instanceof String cStr) {
return !cStr.isEmpty() ? cStr.charAt(0) : null;
String trimmed = cStr.trim();
String source = trimmed.isEmpty() ? cStr : trimmed;
return !source.isEmpty() ? source.charAt(0) : null;
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,72 @@ public void testStringToEnum() {
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, "red", FurColor.class));
}

public void testStringToNumberIgnoresSurroundingWhitespace() {
assertEquals(42L, converter.convertValue(context, null, null, null, " 42 ", Long.class));
assertEquals(42L, converter.convertValue(context, null, null, null, new String[]{"\t42\n"}, Long.class));
assertEquals(42, converter.convertValue(context, null, null, null, " 42 ", Integer.class));
assertEquals((short) 42, converter.convertValue(context, null, null, null, " 42 ", Short.class));
assertEquals(4.2f, converter.convertValue(context, null, null, null, " 4.2 ", Float.class));
assertEquals(4.2, converter.convertValue(context, null, null, null, " 4.2 ", Double.class));
assertEquals(4.2, converter.convertValue(context, null, null, null, " 4.2 ", double.class));
assertEquals(new BigInteger("42"), converter.convertValue(context, null, null, null, " 42 ", BigInteger.class));
assertEquals(new BigDecimal("4.2"), converter.convertValue(context, null, null, null, " 4.2 ", BigDecimal.class));
}

public void testStringToEnumIgnoresSurroundingWhitespace() {
assertEquals(FurColor.BLACK, converter.convertValue(context, null, null, null, " BLACK ", FurColor.class));
assertEquals(FurColor.BLACK, converter.convertValue(context, null, null, null, new String[]{"BLACK\t"}, FurColor.class));
}

public void testStringToBooleanIgnoresSurroundingWhitespace() {
assertEquals(Boolean.TRUE, converter.convertValue(context, null, null, null, " true ", Boolean.class));
assertEquals(Boolean.TRUE, converter.convertValue(context, null, null, null, " true ", boolean.class));
}

public void testStringToCharacterIgnoresSurroundingWhitespace() {
assertEquals('x', converter.convertValue(context, null, null, null, " x", Character.class));
assertEquals('x', converter.convertValue(context, null, null, null, " x", char.class));
}

public void testStringToJavaTimeIgnoresSurroundingWhitespace() {
assertEquals(LocalDate.of(2001, 1, 10), converter.convertValue(context, null, null, null, " 2001-01-10 ", LocalDate.class));
assertEquals(LocalDateTime.of(2001, 1, 10, 1, 2, 3),
converter.convertValue(context, null, null, null, " 2001-01-10T01:02:03 ", LocalDateTime.class));
}

public void testStringArrayToArrayIgnoresSurroundingWhitespace() {
assertArrayEquals(new Long[]{1L, 2L}, (Long[]) converter.convertValue(context, null, null, null, new String[]{" 1 ", "2 "}, Long[].class));
}

public void testWhitespaceOnlyConvertsToNullForBoxedTypes() {
assertNull(converter.convertValue(context, null, null, null, " ", Long.class));
assertNull(converter.convertValue(context, null, null, null, " ", Integer.class));
assertNull(converter.convertValue(context, null, null, null, " ", Double.class));
assertNull(converter.convertValue(context, null, null, null, " ", BigDecimal.class));
assertNull(converter.convertValue(context, null, null, null, " ", FurColor.class));
assertNull(converter.convertValue(context, null, null, null, " ", Boolean.class));
assertNull(converter.convertValue(context, null, null, null, " ", LocalDate.class));
}

public void testWhitespaceOnlyIsConversionErrorForIntegralPrimitives() {
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, " ", long.class));
assertEquals(OgnlRuntime.NoConversionPossible, converter.convertValue(context, null, null, null, " ", int.class));
}

public void testStringTargetsKeepSurroundingWhitespace() {
assertEquals(" keep ", converter.convertValue(context, null, null, null, " keep ", String.class));
assertEquals(" keep ", converter.convertValue(context, null, null, null, " keep ", Object.class));
assertArrayEquals(new String[]{" keep "}, (String[]) converter.convertValue(context, null, null, null, new String[]{" keep "}, String[].class));
}

public void testCustomTypeConverterReceivesUntrimmedValue() {
XWorkConverter freshConverter = container.inject(XWorkConverter.class);
freshConverter.setTypeConverterHolder(new StrutsTypeConverterHolder());
freshConverter.registerConverter(FurColor.class.getName(), (ctx, target, member, property, value, toType) -> value);

assertEquals(" BLACK ", freshConverter.convertValue(context, null, null, null, " BLACK ", FurColor.class));
}

// Testing for null result on non-primitive Number types supplied as empty String or
public void testNotPrimitiveDefaultsToNull() {
assertNull(converter.convertValue(context, null, null, null, null, Double.class));
Expand Down
Loading