Skip to content

Commit 61e98d7

Browse files
committed
Support enum parameters with custom labels
With this change, an enum class can implement toString() to control how it is presented in the user interface.
1 parent 9abafb2 commit 61e98d7

File tree

4 files changed

+105
-6
lines changed

4 files changed

+105
-6
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</parent>
1111

1212
<artifactId>scijava-common</artifactId>
13-
<version>2.96.1-SNAPSHOT</version>
13+
<version>2.97.0-SNAPSHOT</version>
1414

1515
<name>SciJava Common</name>
1616
<description>SciJava Common is a shared library for SciJava software. It provides a plugin framework, with an extensible mechanism for service discovery, backed by its own annotation processor, so that plugins can be loaded dynamically. It is used by downstream projects in the SciJava ecosystem, such as ImageJ and SCIFIO.</description>

src/main/java/org/scijava/convert/DefaultConverter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public Object convert(final Object src, final Type dest) {
135135
// special case for conversion to enum
136136
if (saneDest.isEnum()) {
137137
try {
138-
return Types.enumValue(s, saneDest);
138+
return Types.enumFromString(s, saneDest);
139139
}
140140
catch (final IllegalArgumentException exc) {
141141
// NB: No action needed.

src/main/java/org/scijava/util/Types.java

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -780,8 +780,10 @@ public static <T> T cast(final Object src, final Class<T> dest) {
780780
}
781781

782782
/**
783-
* Converts the given string value to an enumeration constant of the specified
784-
* type.
783+
* Converts the given string <em>value</em> to an enumeration constant of the
784+
* specified type. For example, {@code enumValue("APPLE", Fruit.class)}
785+
* returns {@code Fruit.APPLE} if such a value is among those of the
786+
* requested enum class.
785787
*
786788
* @param name The value to convert.
787789
* @param dest The type of the enumeration constant.
@@ -798,6 +800,60 @@ public static <T> T enumValue(final String name, final Class<T> dest) {
798800
return typedResult;
799801
}
800802

803+
/**
804+
* Converts the given string <em>label</em> to an enumeration constant of the
805+
* specified type. An enum label is the string returned by the enum constant's
806+
* {@link Object#toString()} method. For example,
807+
* {@code enumFromLabel("Apple", Fruit.class)} returns {@code Fruit.APPLE} if
808+
* {@code Fruit.APPLE.toString()} is implemented to return {@code "Apple"}.
809+
*
810+
* @param label The {@code toString()} result of the desired enum value.
811+
* @param dest The type of the enumeration constant.
812+
* @return The matching enumeration constant.
813+
* @throws IllegalArgumentException if the type is not an enumeration type, or
814+
* has no constant with the given label.
815+
*/
816+
public static <T> T enumFromLabel(final String label, final Class<T> dest) {
817+
final T[] values = dest.getEnumConstants();
818+
if (values == null) throw iae("Not an enum type: " + name(dest));
819+
for (T value : values) {
820+
if (Objects.equals(label, value.toString())) return value;
821+
}
822+
throw iae("Enum class " + dest.getName() + " has no such label: " + label);
823+
}
824+
825+
/**
826+
* Converts the given string value or label to an enumeration constant of the
827+
* specified type.
828+
* <p>
829+
* If the string matches one of the enum values directly, that value will be
830+
* returned via {@link #enumValue(String, Class)}. Otherwise, the result of
831+
* {@link #enumFromLabel} is returned.
832+
* </p>
833+
*
834+
* @param s The name or label of the desired enum value.
835+
* @param dest The type of the enumeration constant.
836+
* @return The matching enumeration constant.
837+
* @throws IllegalArgumentException if the type is not an enumeration type,
838+
* or has no such constant with the given name nor label.
839+
*/
840+
public static <T> T enumFromString(final String s, final Class<T> dest) {
841+
if (!dest.isEnum()) throw iae("Not an enum type: " + name(dest));
842+
try {
843+
return enumValue(s, dest);
844+
}
845+
catch (final IllegalArgumentException exc) {
846+
// NB: No action needed.
847+
}
848+
try {
849+
return enumFromLabel(s, dest);
850+
}
851+
catch (final IllegalArgumentException exc) {
852+
// NB: No action needed.
853+
}
854+
throw iae("Enum class " + dest.getName() + " has no such value nor label: " + s);
855+
}
856+
801857
/**
802858
* Creates a new {@link ParameterizedType} of the given class together with
803859
* the specified type arguments.

src/test/java/org/scijava/util/TypesTest.java

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ public void testEnumValue() {
593593
/** Tests {@link Types#enumValue(String, Class)} for invalid value. */
594594
@Test(expected = IllegalArgumentException.class)
595595
public void testEnumValueNoConstant() {
596-
Types.enumValue("NONE", Words.class);
596+
Types.enumValue("OMG", Words.class);
597597
}
598598

599599
/** Tests {@link Types#enumValue(String, Class)} for non-enum class. */
@@ -602,6 +602,38 @@ public void testEnumValueNonEnum() {
602602
Types.enumValue("HOOYAH", String.class);
603603
}
604604

605+
/** Tests {@link Types#enumFromLabel(String, Class)}. */
606+
@Test
607+
public void testEnumFromLabel() {
608+
final Words foo = Types.enumFromLabel("Foo", Words.class);
609+
assertSame(Words.FOO, foo);
610+
final Words bar = Types.enumFromLabel("Bar", Words.class);
611+
assertSame(Words.BAR, bar);
612+
final Words fubar = Types.enumFromLabel("OMG", Words.class);
613+
assertSame(Words.FUBAR, fubar);
614+
}
615+
616+
/** Tests {@link Types#enumFromString(String, Class)}. */
617+
@Test
618+
public void testEnumFromString() {
619+
{
620+
final Words foo = Types.enumFromString("FOO", Words.class);
621+
assertSame(Words.FOO, foo);
622+
final Words bar = Types.enumFromString("BAR", Words.class);
623+
assertSame(Words.BAR, bar);
624+
final Words fubar = Types.enumFromString("FUBAR", Words.class);
625+
assertSame(Words.FUBAR, fubar);
626+
}
627+
{
628+
final Words foo = Types.enumFromString("Foo", Words.class);
629+
assertSame(Words.FOO, foo);
630+
final Words bar = Types.enumFromString("Bar", Words.class);
631+
assertSame(Words.BAR, bar);
632+
final Words fubar = Types.enumFromString("OMG", Words.class);
633+
assertSame(Words.FUBAR, fubar);
634+
}
635+
}
636+
605637
/** Tests {@link Types#parameterize(Class, Map)}. */
606638
@Test
607639
public void testParameterizeMap() {
@@ -644,7 +676,18 @@ private static class ComplexThing<T extends Serializable & Cloneable> extends
644676

645677
/** Enumeration for testing conversion to enum types. */
646678
public static enum Words {
647-
FOO, BAR, FUBAR
679+
FOO("Foo"), BAR("Bar"), FUBAR("OMG");
680+
681+
private final String label;
682+
683+
private Words(final String label) {
684+
this.label = label;
685+
}
686+
687+
@Override
688+
public String toString() {
689+
return label;
690+
}
648691
}
649692

650693
private interface TestTypes<T extends Number> {

0 commit comments

Comments
 (0)