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> on the reasoning behind the change.</li>
<li>Grid SelectionModels are now Extensions. This update removes all selection related variables and API from <li>Grid SelectionModels are now Extensions. This update removes all selection related variables and API from
GridConnector, GridState, GridServerRpc and GridClientRpc</li> 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> </ul>
<h3 id="knownissues">Known Issues and Limitations</h3> <h3 id="knownissues">Known Issues and Limitations</h3>
<ul> <ul>
Expand Down
Expand Up @@ -22,8 +22,15 @@
* A converter that converts from {@link String} to an {@link Enum} and back. * A converter that converts from {@link String} to an {@link Enum} and back.
* <p> * <p>
* Designed to provide nice human readable strings for {@link Enum} classes * Designed to provide nice human readable strings for {@link Enum} classes
* where the constants are named SOME_UPPERCASE_WORDS. Will not necessarily work * conforming to one of these patterns:
* correctly for other cases. * <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> * </p>
* *
* @author Vaadin Ltd * @author Vaadin Ltd
Expand Down Expand Up @@ -63,26 +70,35 @@ public static <T extends Enum<T>> T stringToEnum(String value,
locale = Locale.getDefault(); locale = Locale.getDefault();
} }


// Foo -> FOO if (!enumType.isEnum()) {
// Foo bar -> FOO_BAR throw new ConversionException(enumType.getName()
String result = value.replace(" ", "_").toUpperCase(locale); + " is not an enum type");
try { }
return Enum.valueOf(enumType, result);
} catch (Exception ee) { // First test for the human-readable value since that's the more likely
// There was no match. Try to compare the available values to see if // input
// the constant is using something else than all upper case String upperCaseValue = value.toUpperCase(locale);
try { T match = null;
EnumSet<T> set = EnumSet.allOf(enumType); for (T e : EnumSet.allOf(enumType)) {
for (T e : set) { String upperCase = enumToString(e, locale).toUpperCase(locale);
if (e.name().toUpperCase(locale).equals(result)) { if (upperCase.equals(upperCaseValue)) {
return e; 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 if (match != null) {
// user knows what went wrong 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); throw new ConversionException(ee);
} }
} }
Expand All @@ -107,12 +123,17 @@ public static String enumToString(Enum<?> value, Locale locale) {
} }


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


@Override @Override
Expand Down
Expand Up @@ -14,10 +14,36 @@ public static enum FooEnum {
VALUE1, SOME_VALUE, FOO_BAR_BAZ, Bar, nonStandardCase, _HUGH; 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(); StringToEnumConverter converter = new StringToEnumConverter();
Converter<Enum, String> reverseConverter = new ReverseConverter<Enum, String>( Converter<Enum, String> reverseConverter = new ReverseConverter<Enum, String>(
converter); 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 @Test
public void testEmptyStringConversion() { public void testEmptyStringConversion() {
Assert.assertEquals(null, 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.