Skip to content

Commit

Permalink
Support custom Enum.toString in converter (#17301)
Browse files Browse the repository at this point in the history
Change-Id: Icd3c164fb252bd048ffcd953f967a9c7acdc4514
  • Loading branch information
Legioth committed Sep 9, 2015
1 parent 8b9cb7b commit 22dfc62
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 25 deletions.
2 changes: 2 additions & 0 deletions WebContent/release-notes.html
Expand Up @@ -115,6 +115,8 @@ <h3 id="incompatible">Incompatible or Behavior-altering Changes in @version-mino
on the reasoning behind the change.</li>
<li>Grid SelectionModels are now Extensions. This update removes all selection related variables and API from
GridConnector, GridState, GridServerRpc and GridClientRpc</li>
<li>StringToEnumConverter now explicitly supports Enum types with custom toString() implementations.
This may affect applications that relied on the undefined behavior in previous versions.</li>
</ul>
<h3 id="knownissues">Known Issues and Limitations</h3>
<ul>
Expand Down
Expand Up @@ -22,8 +22,15 @@
* A converter that converts from {@link String} to an {@link Enum} and back.
* <p>
* Designed to provide nice human readable strings for {@link Enum} classes
* where the constants are named SOME_UPPERCASE_WORDS. Will not necessarily work
* correctly for other cases.
* conforming to one of these patterns:
* <ul>
* <li>The constants are named SOME_UPPERCASE_WORDS and there's no toString
* implementation.</li>
* <li>toString() always returns the same human readable string that is not the
* same as its name() value. Each constant in the enum type returns a distinct
* toString() value.</li>
* </ul>
* Will not necessarily work correctly for other cases.
* </p>
*
* @author Vaadin Ltd
Expand Down Expand Up @@ -63,26 +70,35 @@ public static <T extends Enum<T>> T stringToEnum(String value,
locale = Locale.getDefault();
}

// Foo -> FOO
// Foo bar -> FOO_BAR
String result = value.replace(" ", "_").toUpperCase(locale);
try {
return Enum.valueOf(enumType, result);
} catch (Exception ee) {
// There was no match. Try to compare the available values to see if
// the constant is using something else than all upper case
try {
EnumSet<T> set = EnumSet.allOf(enumType);
for (T e : set) {
if (e.name().toUpperCase(locale).equals(result)) {
return e;
}
if (!enumType.isEnum()) {
throw new ConversionException(enumType.getName()
+ " is not an enum type");
}

// First test for the human-readable value since that's the more likely
// input
String upperCaseValue = value.toUpperCase(locale);
T match = null;
for (T e : EnumSet.allOf(enumType)) {
String upperCase = enumToString(e, locale).toUpperCase(locale);
if (upperCase.equals(upperCaseValue)) {
if (match != null) {
throw new ConversionException("Both " + match.name()
+ " and " + e.name()
+ " are matching the input string " + value);
}
} catch (Exception e) {
match = e;
}
}

// Fallback did not work either, re-throw original exception so
// user knows what went wrong
if (match != null) {
return match;
}

// Then fall back to using a strict match based on name()
try {
return Enum.valueOf(enumType, upperCaseValue);
} catch (Exception ee) {
throw new ConversionException(ee);
}
}
Expand All @@ -107,12 +123,17 @@ public static String enumToString(Enum<?> value, Locale locale) {
}

String enumString = value.toString();
// FOO -> Foo
// FOO_BAR -> Foo bar
// _FOO -> _foo
String result = enumString.substring(0, 1).toUpperCase(locale);
result += enumString.substring(1).toLowerCase(locale).replace('_', ' ');
return result;
if (enumString.equals(value.name())) {
// FOO -> Foo
// FOO_BAR -> Foo bar
// _FOO -> _foo
String result = enumString.substring(0, 1).toUpperCase(locale);
result += enumString.substring(1).toLowerCase(locale)
.replace('_', ' ');
return result;
} else {
return enumString;
}
}

@Override
Expand Down
Expand Up @@ -14,10 +14,36 @@ public static enum FooEnum {
VALUE1, SOME_VALUE, FOO_BAR_BAZ, Bar, nonStandardCase, _HUGH;
}

public static enum EnumWithCustomToString {
ONE, TWO, THREE;

@Override
public String toString() {
return "case " + (ordinal() + 1);
}
}

public static enum EnumWithAmbigousToString {
FOO, FOOBAR, FOO_BAR;

@Override
public String toString() {
return name().replaceAll("_", "");
}
}

StringToEnumConverter converter = new StringToEnumConverter();
Converter<Enum, String> reverseConverter = new ReverseConverter<Enum, String>(
converter);

private String convertToString(Enum value) {
return converter.convertToPresentation(value, String.class, null);
}

public Enum convertToEnum(String string, Class<? extends Enum> type) {
return converter.convertToModel(string, type, null);
}

@Test
public void testEmptyStringConversion() {
Assert.assertEquals(null,
Expand Down Expand Up @@ -79,4 +105,31 @@ public void testReverseValueConversion() {

}

@Test
public void preserveFormattingWithCustomToString() {
for (EnumWithCustomToString e : EnumWithCustomToString.values()) {
Assert.assertEquals(e.toString(), convertToString(e));
}
}

@Test
public void findEnumWithCustomToString() {
for (EnumWithCustomToString e : EnumWithCustomToString.values()) {
Assert.assertSame(e,
convertToEnum(e.toString(), EnumWithCustomToString.class));
Assert.assertSame(e,
convertToEnum(e.name(), EnumWithCustomToString.class));
}
}

@Test
public void unambigousValueInEnumWithAmbigous_succeed() {
Assert.assertSame(EnumWithAmbigousToString.FOO,
convertToEnum("foo", EnumWithAmbigousToString.class));
}

@Test(expected = ConversionException.class)
public void ambigousValue_throws() {
convertToEnum("foobar", EnumWithAmbigousToString.class);
}
}

0 comments on commit 22dfc62

Please sign in to comment.