Skip to content

Commit

Permalink
- Support String containing a single UNICODE character to be converti…
Browse files Browse the repository at this point in the history
…ble to char/Character

- "true" (with quotes) as a String is also converted to true on String to boolean types.
- Better error messages (comma handling on lists for Map conversions, highlighting what keys are valid)
  • Loading branch information
jdereg committed Apr 15, 2024
1 parent 221dffb commit 6cbdf78
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 11 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<groupId>com.cedarsoftware</groupId>
<artifactId>java-util</artifactId>
<packaging>bundle</packaging>
<version>2.5.0</version>
<version>2.6.0-SNAPSHOT</version>
<description>Java Utilities</description>
<url>https://github.com/jdereg/java-util</url>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,11 @@ private static <T> T fromMap(Object from, Converter converter, Class<T> type, St
builder.append(", ");
}

builder.append("[value], or [_v] as keys with associated values.");
builder.append("[value]");
if (keySets.length > 0) {
builder.append(",");
}
builder.append(" or [_v] as keys with associated values.");
throw new IllegalArgumentException(builder.toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ final class StringConversions {
private static final BigDecimal bigDecimalMaxLong = BigDecimal.valueOf(Long.MAX_VALUE);
private static final BigDecimal bigDecimalMinLong = BigDecimal.valueOf(Long.MIN_VALUE);
private static final Pattern MM_DD = Pattern.compile("^(\\d{1,2}).(\\d{1,2})$");
private static final Pattern allDigits = Pattern.compile("^\\d+$");

private StringConversions() {}

Expand Down Expand Up @@ -198,7 +199,7 @@ static Boolean toBoolean(Object from, Converter converter) {
} else if ("false".equals(str)) {
return false;
}
return "true".equalsIgnoreCase(str) || "t".equalsIgnoreCase(str) || "1".equals(str) || "y".equalsIgnoreCase(str);
return "true".equalsIgnoreCase(str) || "t".equalsIgnoreCase(str) || "1".equals(str) || "y".equalsIgnoreCase(str) || "\"true\"".equalsIgnoreCase(str);
}

static char toCharacter(Object from, Converter converter) {
Expand All @@ -209,12 +210,27 @@ static char toCharacter(Object from, Converter converter) {
if (str.length() == 1) {
return str.charAt(0);
}
// Treat as a String number, like "65" = 'A'
try {
return (char) Integer.parseInt(str.trim());
} catch (Exception e) {
throw new IllegalArgumentException("Unable to parse '" + from + "' as a Character.", e);

Matcher matcher = allDigits.matcher(str);
boolean isAllDigits = matcher.matches();
if (isAllDigits) {
try { // Treat as a String number, like "65" = 'A'
return (char) Integer.parseInt(str.trim());
} catch (Exception e) {
throw new IllegalArgumentException("Unable to parse '" + from + "' as a Character.", e);
}
}

char result = parseUnicodeEscape(str);
return result;
}

private static char parseUnicodeEscape(String unicodeStr) throws IllegalArgumentException {
if (!unicodeStr.startsWith("\\u") || unicodeStr.length() != 6) {
throw new IllegalArgumentException("Invalid Unicode escape sequence: " + unicodeStr);
}
int codePoint = Integer.parseInt(unicodeStr.substring(2), 16);
return (char) codePoint;
}

static BigInteger toBigInteger(Object from, Converter converter) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2795,7 +2795,7 @@ void testMapToAtomicBoolean()
map.clear();
assertThatThrownBy(() -> this.converter.convert(map, AtomicBoolean.class))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Map to 'AtomicBoolean' the map must include: [value], or [_v] as keys with associated values");
.hasMessageContaining("Map to 'AtomicBoolean' the map must include: [value] or [_v] as keys with associated values");
}

@Test
Expand All @@ -2818,7 +2818,7 @@ void testMapToAtomicInteger()
map.clear();
assertThatThrownBy(() -> this.converter.convert(map, AtomicInteger.class))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Map to 'AtomicInteger' the map must include: [value], or [_v] as keys with associated values");
.hasMessageContaining("Map to 'AtomicInteger' the map must include: [value] or [_v] as keys with associated values");
}

@Test
Expand All @@ -2841,7 +2841,7 @@ void testMapToAtomicLong()
map.clear();
assertThatThrownBy(() -> this.converter.convert(map, AtomicLong.class))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Map to 'AtomicLong' the map must include: [value], or [_v] as keys with associated values");
.hasMessageContaining("Map to 'AtomicLong' the map must include: [value] or [_v] as keys with associated values");
}

@ParameterizedTest
Expand Down

0 comments on commit 6cbdf78

Please sign in to comment.