diff --git a/common/api/core/src/main/java/com/dfsek/terra/api/config/ConfigPack.java b/common/api/core/src/main/java/com/dfsek/terra/api/config/ConfigPack.java index eec7d3be2..63c984878 100644 --- a/common/api/core/src/main/java/com/dfsek/terra/api/config/ConfigPack.java +++ b/common/api/core/src/main/java/com/dfsek/terra/api/config/ConfigPack.java @@ -62,5 +62,11 @@ public interface ConfigPack extends LoaderRegistrar, LoaderHolder, RegistryHolde RegistryFactory getRegistryFactory(); + ConfigPack registerShortcut(Type clazz, String shortcut, ShortcutLoader loader); + + default ConfigPack registerShortcut(Class clazz, String shortcut, ShortcutLoader loader) { + return registerShortcut((Type) clazz, shortcut, loader); + } + ChunkGeneratorProvider getGeneratorProvider(); } diff --git a/common/api/core/src/main/java/com/dfsek/terra/api/config/ShortcutLoader.java b/common/api/core/src/main/java/com/dfsek/terra/api/config/ShortcutLoader.java new file mode 100644 index 000000000..57fef7a8b --- /dev/null +++ b/common/api/core/src/main/java/com/dfsek/terra/api/config/ShortcutLoader.java @@ -0,0 +1,8 @@ +package com.dfsek.terra.api.config; + +import com.dfsek.tectonic.api.loader.ConfigLoader; + + +public interface ShortcutLoader { + T load(ConfigLoader configLoader, String input); +} diff --git a/common/implementation/base/src/main/java/com/dfsek/terra/config/pack/ConfigPackImpl.java b/common/implementation/base/src/main/java/com/dfsek/terra/config/pack/ConfigPackImpl.java index 00a3b549d..10accb243 100644 --- a/common/implementation/base/src/main/java/com/dfsek/terra/config/pack/ConfigPackImpl.java +++ b/common/implementation/base/src/main/java/com/dfsek/terra/config/pack/ConfigPackImpl.java @@ -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, CheckedRegistry>> registryMap = new HashMap<>(); + private final Map> registryMap = new HashMap<>(); + private final Map> shortcuts = new HashMap<>(); - private final ConfigTypeRegistry configTypeRegistry; + private final OpenRegistry> configTypeRegistry; private final TreeMap>>> 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 ConfigPack registerShortcut(Type clazz, String shortcut, ShortcutLoader 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) openRegistry)::register); + private OpenRegistry> 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, CheckedRegistry>> getRegistryMap() { - return registryMap; - } - public ConfigPackTemplate getTemplate() { return template; } @@ -402,18 +398,13 @@ public class ConfigPackImpl implements ConfigPack { @Override @SuppressWarnings("unchecked") public CheckedRegistry getRegistry(Type type) { - return (CheckedRegistry) registryMap.getOrDefault(type, Pair.ofNull()).getRight(); + return (CheckedRegistry) registryMap.get(type); } @SuppressWarnings("unchecked") @Override public CheckedRegistry getCheckedRegistry(Type type) throws IllegalStateException { - return (CheckedRegistry) registryMap.getOrDefault(type, Pair.ofNull()).getRight(); - } - - @SuppressWarnings("unchecked") - protected OpenRegistry getOpenRegistry(Class clazz) { - return (OpenRegistry) registryMap.getOrDefault(clazz, Pair.ofNull()).getLeft(); + return (CheckedRegistry) registryMap.get(type); } @Override diff --git a/common/implementation/base/src/main/java/com/dfsek/terra/registry/CheckedRegistryImpl.java b/common/implementation/base/src/main/java/com/dfsek/terra/registry/CheckedRegistryImpl.java index 8e2c85d7d..f210a1b10 100644 --- a/common/implementation/base/src/main/java/com/dfsek/terra/registry/CheckedRegistryImpl.java +++ b/common/implementation/base/src/main/java/com/dfsek/terra/registry/CheckedRegistryImpl.java @@ -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 implements CheckedRegistry { private final OpenRegistry registry; + @Internal + public OpenRegistry getRegistry() { + return registry; + } + public CheckedRegistryImpl(OpenRegistry registry) { this.registry = registry; } @@ -81,7 +87,7 @@ public class CheckedRegistryImpl implements CheckedRegistry { } @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); } } diff --git a/common/implementation/base/src/main/java/com/dfsek/terra/registry/ShortcutHolder.java b/common/implementation/base/src/main/java/com/dfsek/terra/registry/ShortcutHolder.java new file mode 100644 index 000000000..7a00b528b --- /dev/null +++ b/common/implementation/base/src/main/java/com/dfsek/terra/registry/ShortcutHolder.java @@ -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 implements TypeLoader { + private final Map> shortcuts = new HashMap<>(); + private final Registry back; + + public ShortcutHolder(Registry back) { + this.back = back; + } + + public ShortcutHolder register(String id, ShortcutLoader 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); + } +} diff --git a/common/implementation/base/src/main/java/com/dfsek/terra/registry/config/ConfigTypeRegistry.java b/common/implementation/base/src/main/java/com/dfsek/terra/registry/config/ConfigTypeRegistry.java deleted file mode 100644 index c24a2f1d4..000000000 --- a/common/implementation/base/src/main/java/com/dfsek/terra/registry/config/ConfigTypeRegistry.java +++ /dev/null @@ -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 . - */ - -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> { - private static final Logger logger = LoggerFactory.getLogger(ConfigTypeRegistry.class); - - private final BiConsumer> callback; - - public ConfigTypeRegistry(Platform platform, BiConsumer> callback) { - super(new LinkedHashMap<>()); // Ordered - this.callback = callback; - } - - @Override - public boolean register(String identifier, Entry> 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); - } -}