diff --git a/api/src/main/java/net/kyori/adventure/util/Index.java b/api/src/main/java/net/kyori/adventure/util/Index.java index 47db3ac51..4b56650c4 100644 --- a/api/src/main/java/net/kyori/adventure/util/Index.java +++ b/api/src/main/java/net/kyori/adventure/util/Index.java @@ -29,9 +29,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Set; import java.util.function.Function; import java.util.function.IntFunction; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -154,6 +156,36 @@ private Index(final Map keyToValue, final Map valueToKey) { return this.valueToKey.get(value); } + /** + * Gets the key for a value or throws an exception. + * + * @param value the value + * @return the key + * @throws NoSuchElementException if there is no key for the value + * @since 4.11.0 + */ + public @NotNull K keyOrThrow(final @NotNull V value) { + final K key = this.key(value); + if (key == null) { + throw new NoSuchElementException("There is no key for value " + value); + } + return key; + } + + /** + * Gets a key by its value or returns a fallback key. + * + * @param value the value + * @param defaultKey the fallback key + * @return the key + * @since 4.11.0 + */ + @Contract("_, null -> null; _, !null -> !null") + public K keyOr(final @NotNull V value, final @Nullable K defaultKey) { + final K key = this.key(value); + return key == null ? defaultKey : key; + } + /** * Gets the keys. * @@ -175,6 +207,36 @@ private Index(final Map keyToValue, final Map valueToKey) { return this.keyToValue.get(key); } + /** + * Gets a value by its key. + * + * @param key the key + * @return the value + * @throws NoSuchElementException if there is no value for the key + * @since 4.11.0 + */ + public @NotNull V valueOrThrow(final @NotNull K key) { + final V value = this.value(key); + if (value == null) { + throw new NoSuchElementException("There is no value for key " + key); + } + return value; + } + + /** + * Gets a value by its key or returns a fallback value. + * + * @param key the key + * @param defaultValue the fallback value + * @return the value + * @since 4.11.0 + */ + @Contract("_, null -> null; _, !null -> !null") + public V valueOr(final @NotNull K key, final @Nullable V defaultValue) { + final V value = this.value(key); + return value == null ? defaultValue : value; + } + /** * Get an unmodifiable mapping of index entries from key to value. * diff --git a/api/src/test/java/net/kyori/adventure/util/IndexTest.java b/api/src/test/java/net/kyori/adventure/util/IndexTest.java index 6219eb350..d8eb857ad 100644 --- a/api/src/test/java/net/kyori/adventure/util/IndexTest.java +++ b/api/src/test/java/net/kyori/adventure/util/IndexTest.java @@ -23,6 +23,7 @@ */ package net.kyori.adventure.util; +import java.util.NoSuchElementException; import java.util.UUID; import org.junit.jupiter.api.Test; @@ -31,7 +32,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; class IndexTest { - private static final Index THINGS = Index.create(Thing.class, thing -> thing.name); + private static final Index THINGS = Index.create(Thing.class, thing -> thing.name, Thing.ABC, Thing.DEF); @Test void testCreateWithNonUniqueKey() { @@ -46,6 +47,7 @@ void testCreateWithNonUniqueValue() { @Test void testKey() { for (final Thing thing : Thing.values()) { + if (thing == Thing.NOT_PRESENT) continue; assertEquals(thing.name, THINGS.key(thing)); } } @@ -53,10 +55,31 @@ void testKey() { @Test void testValue() { for (final Thing thing : Thing.values()) { + if (thing == Thing.NOT_PRESENT) continue; assertEquals(thing, THINGS.value(thing.name)); } } + @Test + void testValueOrThrow() { + assertThrows(NoSuchElementException.class, () -> THINGS.valueOrThrow("__NO_VALUE__")); + } + + @Test + void testKeyOrThrow() { + assertThrows(NoSuchElementException.class, () -> THINGS.keyOrThrow(Thing.NOT_PRESENT)); + } + + @Test + void testValueOrDefault() { + assertEquals(Thing.NOT_PRESENT, THINGS.valueOr("__NO_VALUE__", Thing.NOT_PRESENT)); + } + + @Test + void testKeyOrDefault() { + assertEquals("__DEFAULT__", THINGS.keyOr(Thing.NOT_PRESENT, "__DEFAULT__")); + } + @Test void testKeys() { assertThat(THINGS.keys()).containsExactly("abc", "def"); @@ -69,7 +92,8 @@ void testValues() { private enum Thing { ABC("abc"), - DEF("def"); + DEF("def"), + NOT_PRESENT("not_present"); private final String name;