LibJF/libjf-config-core-v2/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java

231 lines
8.5 KiB
Java

package io.gitlab.jfronny.libjf.config.impl.dsl;
import io.gitlab.jfronny.commons.throwable.Coerce;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.libjf.LibJf;
import io.gitlab.jfronny.libjf.config.api.v2.*;
import io.gitlab.jfronny.libjf.config.api.v2.dsl.CategoryBuilder;
import io.gitlab.jfronny.libjf.config.api.v2.dsl.Migration;
import io.gitlab.jfronny.libjf.config.api.v2.type.Type;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class CategoryBuilderImpl<Builder extends CategoryBuilderImpl<Builder>> implements CategoryBuilder<Builder> {
public final List<CategoryBuilder<?>> categories = new LinkedList<>();
public final String id;
public final String categoryPath;
public String translationPrefix;
public final List<EntryInfo<?>> entries = new LinkedList<>();
public final Map<String, Consumer<ConfigCategory>> presets = new LinkedHashMap<>();
public final List<Supplier<List<ConfigInstance>>> referencedConfigs = new LinkedList<>();
public final List<Consumer<ConfigCategory>> verifiers = new LinkedList<>();
public final Map<String, Consumer<JsonReader>> migrations = new LinkedHashMap<>();
private boolean built = false;
public CategoryBuilderImpl(String id, String categoryPath) {
this.id = id;
this.categoryPath = categoryPath;
this.translationPrefix = id + ".jfconfig." + categoryPath;
addPreset(CONFIG_PRESET_DEFAULT, ConfigCategory::reset);
}
@Override
public Builder setTranslationPrefix(String translationPrefix) {
checkBuilt();
this.translationPrefix = translationPrefix;
return asBuilder();
}
@Override
public String getTranslationPrefix() {
checkBuilt();
return translationPrefix;
}
@Override
public Builder addPreset(String id, Consumer<ConfigCategory> action) {
checkBuilt();
if (presets.containsKey(id)) LibJf.LOGGER.warn("Duplicate preset registered for " + categoryPath + this.id + ": " + id + ", overriding");
presets.put(id, action);
return asBuilder();
}
@Override
public Builder addPreset(String id, Runnable preset) {
return addPreset(id, cfg -> preset.run());
}
@Override
public Builder removePreset(String id) {
checkBuilt();
if (presets.remove(id) == null) throw new NoSuchElementException();
return asBuilder();
}
@Override
public Builder addVerifier(Consumer<ConfigCategory> verifier) {
checkBuilt();
verifiers.add(verifier);
return asBuilder();
}
@Override
public Builder addVerifier(Runnable verifier) {
return addVerifier(cfg -> verifier.run());
}
@Override
public Builder referenceConfig(String id) {
return referenceConfig(() -> List.of(ConfigHolder.getInstance().get(id)));
}
@Override
public Builder referenceConfig(ConfigInstance config) {
return referenceConfig(() -> List.of(config));
}
@Override
public Builder referenceConfig(Supplier<List<ConfigInstance>> gen) {
checkBuilt();
referencedConfigs.add(Objects.requireNonNull(gen));
return asBuilder();
}
@Override
public Builder category(String id, CategoryBuilderFunction builder) {
checkBuilt();
categories.add(builder.apply(new CategoryBuilderImpl(id, categoryPath + id + ".").setTranslationPrefix(translationPrefix + id + ".")));
return asBuilder();
}
@Override
public Builder value(String id, int def, double min, double max, Supplier<Integer> get, Consumer<Integer> set) {
checkBuilt();
entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, Type.TInt.INSTANCE, 100, min, max));
return asBuilder();
}
@Override
public Builder value(String id, long def, double min, double max, Supplier<Long> get, Consumer<Long> set) {
checkBuilt();
entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, Type.TLong.INSTANCE, 100, min, max));
return asBuilder();
}
@Override
public Builder value(String id, float def, double min, double max, Supplier<Float> get, Consumer<Float> set) {
checkBuilt();
entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, Type.TFloat.INSTANCE, 100, min, max));
return asBuilder();
}
@Override
public Builder value(String id, double def, double min, double max, Supplier<Double> get, Consumer<Double> set) {
checkBuilt();
entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, Type.TDouble.INSTANCE, 100, min, max));
return asBuilder();
}
@Override
public Builder value(String id, String def, Supplier<String> get, Consumer<String> set) {
checkBuilt();
entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, Type.TString.INSTANCE));
return asBuilder();
}
@Override
public Builder value(String id, boolean def, Supplier<Boolean> get, Consumer<Boolean> set) {
checkBuilt();
entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, Type.TBool.INSTANCE));
return asBuilder();
}
@Override
public Builder value(String id, String def, String[] options, Supplier<String> get, Consumer<String> set) {
checkBuilt();
entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, Type.TEnum.create(id, options)));
return asBuilder();
}
@Override
public <T extends Enum<T>> Builder value(String id, T def, Class<T> klazz, Supplier<T> get, Consumer<T> set) {
checkBuilt();
entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, new Type.TEnum<>(klazz)));
return asBuilder();
}
@Override
public <T> Builder value(String id, T def, double min, double max, Type type, int width, Supplier<T> get, Consumer<T> set) {
checkBuilt();
entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, type, width, min, max));
return asBuilder();
}
@Override
public <T> Builder value(EntryInfo<T> entry) {
checkBuilt();
entries.add(Objects.requireNonNull(entry));
return asBuilder();
}
@Override
public Builder addMigration(String element, Migration migration) {
checkBuilt();
if (migrations.containsKey(element)) LibJf.LOGGER.warn("Duplicate migration registered for " + categoryPath + id + ": " + element + ", overriding");
migrations.put(element, Coerce.consumer(migration::apply).addHandler(e -> LibJf.LOGGER.error("Could not apply migration for " + element, e)));
return asBuilder();
}
@Override
public String getId() {
return id;
}
protected Builder asBuilder() {
//noinspection unchecked
return (Builder) this;
}
protected void checkBuilt() {
if (built) throw new IllegalStateException("This builder was already used to build a category!");
}
protected void markBuilt() {
checkBuilt();
built = true;
}
@Override
public DslConfigCategory build(Supplier<ConfigInstance> root) {
markBuilt();
Set<String> migrationNames = migrations.keySet();
Set<String> entryNames = new HashSet<>();
for (EntryInfo<?> entry : entries) {
String name = entry.getName();
if (migrationNames.contains(name)) LibJf.LOGGER.warn("Entry conflict: " + categoryPath + id + " contains both a migration and an entry for " + name);
if (!entryNames.add(name)) LibJf.LOGGER.warn("Entry conflict: " + categoryPath + id + " contains two entries for " + name);
}
Set<String> categoryNames = new HashSet<>();
for (CategoryBuilder<?> category : categories) {
String name = category.getId();
if (migrationNames.contains(name)) LibJf.LOGGER.warn("Entry conflict: " + categoryPath + id + " contains both a migration and a category for " + name);
if (entryNames.contains(name)) LibJf.LOGGER.warn("Entry conflict: " + categoryPath + id + " contains both an entry and a category for " + name);
if (!categoryNames.add(name)) LibJf.LOGGER.warn("Entry conflict: " + categoryPath + id + " contains two categories for " + name);
}
return new DslConfigCategory(
id,
categoryPath,
translationPrefix,
entries,
presets,
referencedConfigs,
categories,
root,
verifiers,
migrations
);
}
}