implement shortcut API

This commit is contained in:
dfsek 2021-12-16 21:54:49 -07:00
parent a0c46aaf7a
commit 28b16943f7
6 changed files with 125 additions and 115 deletions

View File

@ -62,5 +62,11 @@ public interface ConfigPack extends LoaderRegistrar, LoaderHolder, RegistryHolde
RegistryFactory getRegistryFactory();
<T> ConfigPack registerShortcut(Type clazz, String shortcut, ShortcutLoader<T> loader);
default <T> ConfigPack registerShortcut(Class<T> clazz, String shortcut, ShortcutLoader<T> loader) {
return registerShortcut((Type) clazz, shortcut, loader);
}
ChunkGeneratorProvider getGeneratorProvider();
}

View File

@ -0,0 +1,8 @@
package com.dfsek.terra.api.config;
import com.dfsek.tectonic.api.loader.ConfigLoader;
public interface ShortcutLoader<T> {
T load(ConfigLoader configLoader, String input);
}

View File

@ -28,35 +28,10 @@ import com.dfsek.tectonic.api.loader.ConfigLoader;
import com.dfsek.tectonic.api.loader.type.TypeLoader;
import com.dfsek.tectonic.impl.abstraction.AbstractConfiguration;
import com.dfsek.tectonic.yaml.YamlConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Supplier;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.config.ConfigFactory;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.ConfigType;
import com.dfsek.terra.api.config.Loader;
import com.dfsek.terra.api.config.*;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.event.events.config.ConfigurationDiscoveryEvent;
import com.dfsek.terra.api.event.events.config.ConfigurationLoadEvent;
@ -79,16 +54,28 @@ import com.dfsek.terra.config.fileloaders.FolderLoader;
import com.dfsek.terra.config.fileloaders.ZIPLoader;
import com.dfsek.terra.config.loaders.GenericTemplateSupplierLoader;
import com.dfsek.terra.config.loaders.config.BufferedImageLoader;
import com.dfsek.terra.config.preprocessor.MetaListLikePreprocessor;
import com.dfsek.terra.config.preprocessor.MetaMapPreprocessor;
import com.dfsek.terra.config.preprocessor.MetaNumberPreprocessor;
import com.dfsek.terra.config.preprocessor.MetaStringPreprocessor;
import com.dfsek.terra.config.preprocessor.MetaValuePreprocessor;
import com.dfsek.terra.config.preprocessor.*;
import com.dfsek.terra.config.prototype.ProtoConfig;
import com.dfsek.terra.registry.CheckedRegistryImpl;
import com.dfsek.terra.registry.OpenRegistryImpl;
import com.dfsek.terra.registry.RegistryFactoryImpl;
import com.dfsek.terra.registry.config.ConfigTypeRegistry;
import com.dfsek.terra.registry.ShortcutHolder;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.function.Supplier;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
@ -110,9 +97,10 @@ public class ConfigPackImpl implements ConfigPack {
private final BiomeProvider seededBiomeProvider;
private final Map<Type, Pair<OpenRegistry<?>, CheckedRegistry<?>>> registryMap = new HashMap<>();
private final Map<Type, CheckedRegistryImpl<?>> registryMap = new HashMap<>();
private final Map<Type, ShortcutHolder<?>> shortcuts = new HashMap<>();
private final ConfigTypeRegistry configTypeRegistry;
private final OpenRegistry<ConfigType<?, ?>> configTypeRegistry;
private final TreeMap<Integer, List<Pair<String, ConfigType<?, ?>>>> configTypes = new TreeMap<>();
public ConfigPackImpl(File folder, Platform platform) {
@ -150,7 +138,7 @@ public class ConfigPackImpl implements ConfigPack {
this.loader = loader;
this.platform = platform;
this.configTypeRegistry = createRegistry();
this.configTypeRegistry = createConfigRegistry();
register(selfLoader);
platform.register(selfLoader);
@ -259,10 +247,10 @@ public class ConfigPackImpl implements ConfigPack {
@Override
public void register(TypeRegistry registry) {
registry
.registerLoader(ConfigType.class, configTypeRegistry)
registry.registerLoader(ConfigType.class, configTypeRegistry)
.registerLoader(BufferedImage.class, new BufferedImageLoader(loader));
registryMap.forEach((clazz, reg) -> registry.registerLoader(clazz, reg.getLeft()));
registryMap.forEach(registry::registerLoader);
shortcuts.forEach(registry::registerLoader); // overwrite with delegated shortcuts if present
}
@Override
@ -335,8 +323,8 @@ public class ConfigPackImpl implements ConfigPack {
}
}
return Pair.of(registry, new CheckedRegistryImpl<>(registry));
}).getRight();
return new CheckedRegistryImpl<>(registry);
});
}
@Override
@ -364,37 +352,45 @@ public class ConfigPackImpl implements ConfigPack {
return registryFactory;
}
@SuppressWarnings("unchecked,rawtypes")
@Override
public <T> ConfigPack registerShortcut(Type clazz, String shortcut, ShortcutLoader<T> loader) {
ShortcutHolder<?> holder = shortcuts
.computeIfAbsent(clazz, c -> new ShortcutHolder<>(getOrCreateRegistry(clazz)))
.register(shortcut, (ShortcutLoader) loader);
selfLoader.registerLoader(clazz, holder);
abstractConfigLoader.registerLoader(clazz, holder);
return this;
}
@Override
public ChunkGeneratorProvider getGeneratorProvider() {
return template.getGeneratorProvider();
}
@SuppressWarnings("unchecked")
private ConfigTypeRegistry createRegistry() {
return new ConfigTypeRegistry(platform, (id, configType) -> {
OpenRegistry<?> openRegistry = configType.registrySupplier(this).get();
if(registryMap.containsKey(configType.getTypeKey()
.getType())) { // Someone already registered something; we need to copy things to the
// new registry.
logger.warn("Copying values from old registry for {}", configType.getTypeKey());
registryMap.get(configType.getTypeKey().getType()).getLeft().forEach(((OpenRegistry<Object>) openRegistry)::register);
private OpenRegistry<ConfigType<?, ?>> createConfigRegistry() {
return new OpenRegistryImpl<>(new LinkedHashMap<>()) {
@Override
public boolean register(@NotNull String identifier, @NotNull ConfigType<?, ?> value) {
if(!registryMap
.containsKey(value.getTypeKey()
.getType())) {
OpenRegistry<?> openRegistry = value.registrySupplier(ConfigPackImpl.this).get();
selfLoader.registerLoader(value.getTypeKey().getType(), openRegistry);
abstractConfigLoader.registerLoader(value.getTypeKey().getType(), openRegistry);
registryMap.put(value.getTypeKey().getType(), new CheckedRegistryImpl<>(openRegistry));
}
return super.register(identifier, value);
}
selfLoader.registerLoader(configType.getTypeKey().getType(), openRegistry);
abstractConfigLoader.registerLoader(configType.getTypeKey().getType(), openRegistry);
registryMap.put(configType.getTypeKey().getType(), Pair.of(openRegistry, new CheckedRegistryImpl<>(openRegistry)));
});
};
}
private void checkDeadEntries() {
registryMap.forEach((clazz, pair) -> ((OpenRegistryImpl<?>) pair.getLeft())
registryMap.forEach((clazz, pair) -> ((OpenRegistryImpl<?>) pair.getRegistry())
.getDeadEntries()
.forEach((id, value) -> logger.debug("Dead entry in '{}' registry: '{}'", ReflectionUtil.typeToString(clazz), id)));
}
protected Map<Type, Pair<OpenRegistry<?>, CheckedRegistry<?>>> getRegistryMap() {
return registryMap;
}
public ConfigPackTemplate getTemplate() {
return template;
}
@ -402,18 +398,13 @@ public class ConfigPackImpl implements ConfigPack {
@Override
@SuppressWarnings("unchecked")
public <T> CheckedRegistry<T> getRegistry(Type type) {
return (CheckedRegistry<T>) registryMap.getOrDefault(type, Pair.ofNull()).getRight();
return (CheckedRegistry<T>) registryMap.get(type);
}
@SuppressWarnings("unchecked")
@Override
public <T> CheckedRegistry<T> getCheckedRegistry(Type type) throws IllegalStateException {
return (CheckedRegistry<T>) registryMap.getOrDefault(type, Pair.ofNull()).getRight();
}
@SuppressWarnings("unchecked")
protected <T> OpenRegistry<T> getOpenRegistry(Class<T> clazz) {
return (OpenRegistry<T>) registryMap.getOrDefault(clazz, Pair.ofNull()).getLeft();
return (CheckedRegistry<T>) registryMap.get(type);
}
@Override

View File

@ -19,6 +19,7 @@ package com.dfsek.terra.registry;
import com.dfsek.tectonic.api.exception.LoadException;
import com.dfsek.tectonic.api.loader.ConfigLoader;
import org.jetbrains.annotations.ApiStatus.Internal;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.AnnotatedType;
@ -41,6 +42,11 @@ import com.dfsek.terra.api.registry.exception.DuplicateEntryException;
public class CheckedRegistryImpl<T> implements CheckedRegistry<T> {
private final OpenRegistry<T> registry;
@Internal
public OpenRegistry<T> getRegistry() {
return registry;
}
public CheckedRegistryImpl(OpenRegistry<T> registry) {
this.registry = registry;
}
@ -81,7 +87,7 @@ public class CheckedRegistryImpl<T> implements CheckedRegistry<T> {
}
@Override
public T load(AnnotatedType t, Object c, ConfigLoader loader) throws LoadException {
public T load(@NotNull AnnotatedType t, @NotNull Object c, @NotNull ConfigLoader loader) throws LoadException {
return registry.load(t, c, loader);
}
}

View File

@ -0,0 +1,48 @@
package com.dfsek.terra.registry;
import com.dfsek.tectonic.api.exception.LoadException;
import com.dfsek.tectonic.api.loader.ConfigLoader;
import com.dfsek.tectonic.api.loader.type.TypeLoader;
import com.dfsek.terra.api.config.ShortcutLoader;
import com.dfsek.terra.api.registry.Registry;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.AnnotatedType;
import java.util.HashMap;
import java.util.Map;
public class ShortcutHolder<T> implements TypeLoader<T> {
private final Map<String, ShortcutLoader<T>> shortcuts = new HashMap<>();
private final Registry<T> back;
public ShortcutHolder(Registry<T> back) {
this.back = back;
}
public ShortcutHolder<T> register(String id, ShortcutLoader<T> loader) {
if(shortcuts.containsKey(id)) {
throw new IllegalArgumentException(
"Attempted to register duplicate shortcut " + id + ", previously registered to " + shortcuts.get(id)
.getClass()
.getCanonicalName());
}
shortcuts.put(id, loader);
return this;
}
@Override
public T load(@NotNull AnnotatedType annotatedType, @NotNull Object o, @NotNull ConfigLoader configLoader) throws LoadException {
String id = (String) o;
if(id.contains(":")) {
String shortcut = id.substring(0, id.indexOf(":"));
if(shortcuts.containsKey(shortcut)) {
return shortcuts.get(shortcut).load(configLoader, id.substring(id.indexOf(":") + 1));
}
throw new LoadException("Shortcut \"" + shortcut + "\" is not defined.");
}
return back.load(annotatedType, o, configLoader);
}
}

View File

@ -1,49 +0,0 @@
/*
* This file is part of Terra.
*
* Terra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Terra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Terra. If not, see <https://www.gnu.org/licenses/>.
*/
package com.dfsek.terra.registry.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.LinkedHashMap;
import java.util.function.BiConsumer;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.config.ConfigType;
import com.dfsek.terra.api.util.reflection.ReflectionUtil;
import com.dfsek.terra.registry.OpenRegistryImpl;
public class ConfigTypeRegistry extends OpenRegistryImpl<ConfigType<?, ?>> {
private static final Logger logger = LoggerFactory.getLogger(ConfigTypeRegistry.class);
private final BiConsumer<String, ConfigType<?, ?>> callback;
public ConfigTypeRegistry(Platform platform, BiConsumer<String, ConfigType<?, ?>> callback) {
super(new LinkedHashMap<>()); // Ordered
this.callback = callback;
}
@Override
public boolean register(String identifier, Entry<ConfigType<?, ?>> value) {
callback.accept(identifier, value.getValue());
logger.debug("Registered config registry with ID {} to type {}", identifier,
ReflectionUtil.typeToString(value.getValue().getTypeKey().getType()));
return super.register(identifier, value);
}
}