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> implements CategoryBuilder { public final List> categories = new LinkedList<>(); public final String id; public final String categoryPath; public String translationPrefix; public final List> entries = new LinkedList<>(); public final Map> presets = new LinkedHashMap<>(); public final List>> referencedConfigs = new LinkedList<>(); public final List> verifiers = new LinkedList<>(); public final Map> 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 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 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> 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 get, Consumer 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 get, Consumer 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 get, Consumer 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 get, Consumer 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 get, Consumer 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 get, Consumer 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 get, Consumer set) { checkBuilt(); entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, Type.TEnum.create(id, options))); return asBuilder(); } @Override public > Builder value(String id, T def, Class klazz, Supplier get, Consumer set) { checkBuilt(); entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, new Type.TEnum<>(klazz))); return asBuilder(); } @Override public Builder value(String id, T def, double min, double max, Type type, int width, Supplier get, Consumer set) { checkBuilt(); entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, type, width, min, max)); return asBuilder(); } @Override public Builder value(EntryInfo 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 root) { markBuilt(); Set migrationNames = migrations.keySet(); Set 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 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 ); } }