implement namespaced registries

This commit is contained in:
dfsek
2021-12-25 01:01:32 -07:00
parent bb87bfa1de
commit 8467a19781
44 changed files with 427 additions and 235 deletions

View File

@@ -10,6 +10,8 @@ import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.registry.exception.NoSuchEntryException;
import com.dfsek.terra.api.registry.key.RegistryKey;
import io.leangen.geantyref.TypeToken;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -92,12 +94,12 @@ public class RegistryArgument<T, R> extends CommandArgument<T, R> {
public @NonNull ArgumentParseResult<@NonNull R> parse(@NonNull CommandContext<@NonNull T> commandContext,
@NonNull Queue<@NonNull String> inputQueue) {
String input = inputQueue.remove();
return registry.get(input).map(ArgumentParseResult::success).orElse(ArgumentParseResult.failure(new NoSuchEntryException("No such entry: " + input)));
return registry.get(RegistryKey.parse(input)).map(ArgumentParseResult::success).orElse(ArgumentParseResult.failure(new NoSuchEntryException("No such entry: " + input)));
}
@Override
public @NonNull List<@NonNull String> suggestions(@NonNull CommandContext<T> commandContext, @NonNull String input) {
return registry.keys().stream().sorted().collect(Collectors.toList());
return registry.keys().stream().map(RegistryKey::toString).sorted().collect(Collectors.toList());
}
}
}

View File

@@ -15,7 +15,9 @@ import java.util.Map;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.registry.key.Keyed;
import com.dfsek.terra.api.registry.key.Namespaced;
import com.dfsek.terra.api.registry.key.RegistryKey;
import com.dfsek.terra.api.registry.meta.RegistryHolder;
import com.dfsek.terra.api.tectonic.ConfigLoadingDelegate;
import com.dfsek.terra.api.tectonic.LoaderRegistrar;
@@ -27,9 +29,9 @@ import com.dfsek.terra.api.world.chunk.generation.stage.GenerationStage;
import com.dfsek.terra.api.world.chunk.generation.util.provider.ChunkGeneratorProvider;
public interface ConfigPack extends LoaderRegistrar, ConfigLoadingDelegate, RegistryHolder, StringIdentifiable, Namespaced {
public interface ConfigPack extends LoaderRegistrar, ConfigLoadingDelegate, RegistryHolder, Keyed {
ConfigPack registerConfigType(ConfigType<?, ?> type, String id, int priority);
ConfigPack registerConfigType(ConfigType<?, ?> type, RegistryKey id, int priority);
Map<BaseAddon, VersionRange> addons();

View File

@@ -7,6 +7,8 @@
package com.dfsek.terra.api.registry;
import com.dfsek.terra.api.registry.key.RegistryKey;
import org.jetbrains.annotations.NotNull;
import com.dfsek.terra.api.registry.exception.DuplicateEntryException;
@@ -21,5 +23,5 @@ public interface CheckedRegistry<T> extends Registry<T> {
*
* @throws DuplicateEntryException If an entry with the same identifier is already present.
*/
void register(@NotNull String identifier, @NotNull T value) throws DuplicateEntryException;
void register(@NotNull RegistryKey identifier, @NotNull T value) throws DuplicateEntryException;
}

View File

@@ -7,6 +7,8 @@
package com.dfsek.terra.api.registry;
import com.dfsek.terra.api.registry.key.RegistryKey;
import org.jetbrains.annotations.NotNull;
import com.dfsek.terra.api.registry.exception.DuplicateEntryException;
@@ -19,7 +21,7 @@ public interface OpenRegistry<T> extends Registry<T> {
* @param identifier Identifier to assign value.
* @param value Value to register.
*/
boolean register(@NotNull String identifier, @NotNull T value);
boolean register(@NotNull RegistryKey identifier, @NotNull T value);
/**
* Add a value to this registry, checking whether it is present first.
@@ -29,7 +31,7 @@ public interface OpenRegistry<T> extends Registry<T> {
*
* @throws DuplicateEntryException If an entry with the same identifier is already present.
*/
void registerChecked(@NotNull String identifier, @NotNull T value) throws DuplicateEntryException;
void registerChecked(@NotNull RegistryKey identifier, @NotNull T value) throws DuplicateEntryException;
/**
* Clears all entries from the registry.

View File

@@ -9,39 +9,41 @@ package com.dfsek.terra.api.registry;
import com.dfsek.tectonic.api.loader.type.TypeLoader;
import com.dfsek.terra.api.registry.key.RegistryKey;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.google.common.reflect.TypeToken;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
public interface Registry<T> extends TypeLoader<T> {
/**
* Get a value from the registry.
*
* @param identifier Identifier of value.
* @param key Identifier of value.
*
* @return Value matching the identifier, {@code null} if no value is present.
*/
@Contract(pure = true)
Optional<T> get(@NotNull String identifier);
Optional<T> get(@NotNull RegistryKey key);
/**
* Check if the registry contains a value.
*
* @param identifier Identifier of value.
* @param key Identifier of value.
*
* @return Whether the registry contains the value.
*/
@Contract(pure = true)
boolean contains(@NotNull String identifier);
boolean contains(@NotNull RegistryKey key);
/**
* Perform the given action for every value in the registry.
@@ -55,7 +57,7 @@ public interface Registry<T> extends TypeLoader<T> {
*
* @param consumer Action to perform on pair.
*/
void forEach(@NotNull BiConsumer<String, T> consumer);
void forEach(@NotNull BiConsumer<RegistryKey, T> consumer);
/**
* Get the entries of this registry as a {@link Set}.
@@ -73,7 +75,30 @@ public interface Registry<T> extends TypeLoader<T> {
*/
@NotNull
@Contract(pure = true)
Set<String> keys();
Set<RegistryKey> keys();
TypeKey<T> getType();
default Optional<T> tryGet(String attempt) {
return get(attempt, map -> {
if(map.isEmpty()) return Optional.empty();
if(map.size() == 1) {
return map.values().stream().findFirst(); // only one value.
}
throw new IllegalArgumentException("ID \"" + attempt + "\" is ambiguous; matches: " + map
.keySet()
.stream()
.map(RegistryKey::toString)
.reduce("", (a, b) -> a + "\n - " + b));
});
}
Map<RegistryKey, T> get(String id);
default Optional<T> get(String attempt, Function<Map<RegistryKey, T>, Optional<T>> reduction) {
if(attempt.contains(":")) {
return get(RegistryKey.parse(attempt));
}
return reduction.apply(get(attempt));
}
}

View File

@@ -0,0 +1,15 @@
package com.dfsek.terra.api.registry.key;
public interface Keyed extends Namespaced, StringIdentifiable {
RegistryKey getRegistryKey();
@Override
default String getNamespace() {
return getRegistryKey().getNamespace();
}
@Override
default String getID() {
return getRegistryKey().getID();
}
}

View File

@@ -3,17 +3,7 @@ package com.dfsek.terra.api.registry.key;
public interface Namespaced {
String getNamespace();
default RegistryKey getKey(String key) {
return new RegistryKey() {
@Override
public String getNamespace() {
return Namespaced.this.getNamespace();
}
@Override
public String getID() {
return key;
}
};
default RegistryKey getKey(String id) {
return RegistryKey.of(getNamespace(), id);
}
}

View File

@@ -1,5 +1,70 @@
package com.dfsek.terra.api.registry.key;
public interface RegistryKey extends StringIdentifiable, Namespaced {
import java.util.Objects;
import java.util.regex.Pattern;
public final class RegistryKey implements StringIdentifiable, Namespaced {
private static final Pattern ID_PATTERN = Pattern.compile("^[a-zA-Z0-9_-]*$");
private final String namespace;
private final String id;
private RegistryKey(String namespace, String id) {
if(!ID_PATTERN.matcher(namespace).matches()) {
throw new IllegalArgumentException(
"Namespace must only contain alphanumeric characters, hyphens, and underscores. \"" + namespace +
"\" is not a valid namespace.");
}
if(!ID_PATTERN.matcher(id).matches()) {
throw new IllegalArgumentException(
"ID must only contain alphanumeric characters, hyphens, and underscores. \"" + id +
"\" is not a valid ID.");
}
this.namespace = namespace;
this.id = id;
}
public static RegistryKey parse(String key) {
if(key.chars().filter(c -> c == ':').count() != 1) {
throw new IllegalArgumentException("Malformed RegistryKey: " + key);
}
String namespace = key.substring(0, key.indexOf(":"));
String id = key.substring(key.indexOf(":") + 1);
return new RegistryKey(namespace, id);
}
public static RegistryKey of(String namespace, String id) {
return new RegistryKey(namespace, id);
}
@Override
public String getNamespace() {
return namespace;
}
@Override
public String getID() {
return id;
}
@Override
public int hashCode() {
return Objects.hash(namespace, id);
}
@Override
public boolean equals(Object obj) {
if(obj instanceof RegistryKey that) {
return this.id.equals(that.id) && this.namespace.equals(that.namespace);
}
return false;
}
@Override
public String toString() {
return namespace + ":" + id;
}
}