645 lines
28 KiB
Java
645 lines
28 KiB
Java
/*
|
|
* Minecraft Forge
|
|
* Copyright (c) 2016-2018.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation version 2.1
|
|
* of the License.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
package net.minecraftforge.common;
|
|
|
|
import static com.electronwill.nightconfig.core.ConfigSpec.CorrectionAction.ADD;
|
|
import static com.electronwill.nightconfig.core.ConfigSpec.CorrectionAction.REMOVE;
|
|
import static com.electronwill.nightconfig.core.ConfigSpec.CorrectionAction.REPLACE;
|
|
import static net.minecraftforge.fml.Logging.CORE;
|
|
|
|
import java.nio.file.Path;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.ListIterator;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.function.Consumer;
|
|
import java.util.function.Function;
|
|
import java.util.function.Predicate;
|
|
import java.util.function.Supplier;
|
|
|
|
import org.apache.commons.lang3.tuple.Pair;
|
|
import org.apache.logging.log4j.LogManager;
|
|
|
|
import com.electronwill.nightconfig.core.CommentedConfig;
|
|
import com.electronwill.nightconfig.core.Config;
|
|
import com.electronwill.nightconfig.core.InMemoryFormat;
|
|
import com.electronwill.nightconfig.core.utils.UnmodifiableConfigWrapper;
|
|
import com.electronwill.nightconfig.core.ConfigSpec.CorrectionAction;
|
|
import com.electronwill.nightconfig.core.ConfigSpec.CorrectionListener;
|
|
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
|
import com.electronwill.nightconfig.core.file.FileConfig;
|
|
import com.electronwill.nightconfig.core.io.WritingMode;
|
|
import com.google.common.base.Joiner;
|
|
import com.google.common.base.Preconditions;
|
|
import com.google.common.base.Splitter;
|
|
import com.google.common.collect.Lists;
|
|
|
|
import io.netty.util.BooleanSupplier;
|
|
|
|
/*
|
|
* Like {@link com.electronwill.nightconfig.core.ConfigSpec} except in builder format, and extended to acept comments, language keys,
|
|
* and other things Forge configs would find useful.
|
|
*/
|
|
|
|
public class ForgeConfigSpec extends UnmodifiableConfigWrapper<Config>
|
|
{
|
|
private Map<List<String>, String> levelComments = new HashMap<>();
|
|
|
|
private Config childConfig;
|
|
|
|
private ForgeConfigSpec(Config storage, Map<List<String>, String> levelComments) {
|
|
super(storage);
|
|
this.levelComments = levelComments;
|
|
}
|
|
|
|
public void setConfig(CommentedConfig config) {
|
|
this.childConfig = config;
|
|
if (!isCorrect(config)) {
|
|
String configName = config instanceof FileConfig ? ((FileConfig) config).getNioPath().toString() : config.toString();
|
|
LogManager.getLogger().warn(CORE, "Configuration file {} is not correct. Correcting", configName);
|
|
correct(config, (action, path, incorrectValue, correctedValue) ->
|
|
LogManager.getLogger().warn(CORE, "Incorrect key {} was corrected from {} to {}", DOT_JOINER.join( path ), incorrectValue, correctedValue));
|
|
if (config instanceof FileConfig) {
|
|
((FileConfig) config).save();
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean isCorrect(CommentedConfig config) {
|
|
LinkedList<String> parentPath = new LinkedList<>();
|
|
return correct(this.config, config, parentPath, Collections.unmodifiableList( parentPath ), null, true) == 0;
|
|
}
|
|
|
|
public int correct(CommentedConfig config) {
|
|
return correct(config, (action, path, incorrectValue, correctedValue) -> {});
|
|
}
|
|
|
|
public int correct(CommentedConfig config, CorrectionListener listener) {
|
|
LinkedList<String> parentPath = new LinkedList<>(); //Linked list for fast add/removes
|
|
return correct(this.config, config, parentPath, Collections.unmodifiableList(parentPath), listener, false);
|
|
}
|
|
|
|
private int correct(Config spec, CommentedConfig config, LinkedList<String> parentPath, List<String> parentPathUnmodifiable, CorrectionListener listener, boolean dryRun)
|
|
{
|
|
int count = 0;
|
|
|
|
Map<String, Object> specMap = spec.valueMap();
|
|
Map<String, Object> configMap = config.valueMap();
|
|
|
|
for (Map.Entry<String, Object> specEntry : specMap.entrySet())
|
|
{
|
|
final String key = specEntry.getKey();
|
|
final Object specValue = specEntry.getValue();
|
|
final Object configValue = configMap.get(key);
|
|
final CorrectionAction action = configValue == null ? ADD : REPLACE;
|
|
|
|
parentPath.addLast(key);
|
|
|
|
if (specValue instanceof Config)
|
|
{
|
|
if (configValue instanceof CommentedConfig)
|
|
{
|
|
count += correct((Config)specValue, (CommentedConfig)configValue, parentPath, parentPathUnmodifiable, listener, dryRun);
|
|
if (count > 0 && dryRun)
|
|
return count;
|
|
}
|
|
else if (dryRun)
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
CommentedConfig newValue = config.createSubConfig();
|
|
configMap.put(key, newValue);
|
|
listener.onCorrect(action, parentPathUnmodifiable, configValue, newValue);
|
|
count++;
|
|
count += correct((Config)specValue, newValue, parentPath, parentPathUnmodifiable, listener, dryRun);
|
|
}
|
|
|
|
String newComment = levelComments.get(parentPath);
|
|
String oldComment = config.getComment(key);
|
|
if (!Objects.equals(oldComment, newComment))
|
|
{
|
|
if (dryRun)
|
|
return 1;
|
|
|
|
//TODO: Comment correction listener?
|
|
config.setComment(key, newComment);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ValueSpec valueSpec = (ValueSpec)specValue;
|
|
if (!valueSpec.test(configValue))
|
|
{
|
|
if (dryRun)
|
|
return 1;
|
|
|
|
Object newValue = valueSpec.correct(configValue);
|
|
configMap.put(key, newValue);
|
|
listener.onCorrect(action, parentPathUnmodifiable, configValue, newValue);
|
|
count++;
|
|
}
|
|
String oldComment = config.getComment(key);
|
|
if (!Objects.equals(oldComment, valueSpec.getComment()))
|
|
{
|
|
if (dryRun)
|
|
return 1;
|
|
|
|
//TODO: Comment correction listener?
|
|
config.setComment(key, valueSpec.getComment());
|
|
}
|
|
}
|
|
|
|
parentPath.removeLast();
|
|
}
|
|
|
|
// Second step: removes the unspecified values
|
|
for (Iterator<Map.Entry<String, Object>> ittr = configMap.entrySet().iterator(); ittr.hasNext();)
|
|
{
|
|
Map.Entry<String, Object> entry = ittr.next();
|
|
if (!specMap.containsKey(entry.getKey()))
|
|
{
|
|
if (dryRun)
|
|
return 1;
|
|
|
|
ittr.remove();
|
|
parentPath.addLast(entry.getKey());
|
|
listener.onCorrect(REMOVE, parentPathUnmodifiable, entry.getValue(), null);
|
|
parentPath.removeLast();
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public static class Builder
|
|
{
|
|
private final Config storage = InMemoryFormat.withUniversalSupport().createConfig();
|
|
private BuilderContext context = new BuilderContext();
|
|
private Map<List<String>, String> levelComments = new HashMap<>();
|
|
private List<String> currentPath = new ArrayList<>();
|
|
private List<ConfigValue<?>> values = new ArrayList<>();
|
|
|
|
//Object
|
|
public <T> ConfigValue<T> define(String path, T defaultValue) {
|
|
return define(split(path), defaultValue);
|
|
}
|
|
public <T> ConfigValue<T> define(List<String> path, T defaultValue) {
|
|
return define(path, defaultValue, o -> o != null && defaultValue.getClass().isAssignableFrom(o.getClass()));
|
|
}
|
|
public <T> ConfigValue<T> define(String path, T defaultValue, Predicate<Object> validator) {
|
|
return define(split(path), defaultValue, validator);
|
|
}
|
|
public <T> ConfigValue<T> define(List<String> path, T defaultValue, Predicate<Object> validator) {
|
|
Objects.requireNonNull(defaultValue, "Default value can not be null");
|
|
return define(path, () -> defaultValue, validator);
|
|
}
|
|
public <T> ConfigValue<T> define(String path, Supplier<T> defaultSupplier, Predicate<Object> validator) {
|
|
return define(split(path), defaultSupplier, validator);
|
|
}
|
|
public <T> ConfigValue<T> define(List<String> path, Supplier<T> defaultSupplier, Predicate<Object> validator) {
|
|
return define(path, defaultSupplier, validator, Object.class);
|
|
}
|
|
public <T> ConfigValue<T> define(List<String> path, Supplier<T> defaultSupplier, Predicate<Object> validator, Class<?> clazz) {
|
|
context.setClazz(clazz);
|
|
return define(path, new ValueSpec(defaultSupplier, validator, context), defaultSupplier);
|
|
}
|
|
public <T> ConfigValue<T> define(List<String> path, ValueSpec value, Supplier<T> defaultSupplier) { // This is the root where everything at the end of the day ends up.
|
|
if (!currentPath.isEmpty()) {
|
|
List<String> tmp = new ArrayList<>(currentPath.size() + path.size());
|
|
tmp.addAll(currentPath);
|
|
tmp.addAll(path);
|
|
path = tmp;
|
|
}
|
|
storage.set(path, value);
|
|
context = new BuilderContext();
|
|
return new ConfigValue<>(this, path, defaultSupplier);
|
|
}
|
|
public <V extends Comparable<? super V>> ConfigValue<V> defineInRange(String path, V defaultValue, V min, V max, Class<V> clazz) {
|
|
return defineInRange(split(path), defaultValue, min, max, clazz);
|
|
}
|
|
public <V extends Comparable<? super V>> ConfigValue<V> defineInRange(List<String> path, V defaultValue, V min, V max, Class<V> clazz) {
|
|
return defineInRange(path, (Supplier<V>)() -> defaultValue, min, max, clazz);
|
|
}
|
|
public <V extends Comparable<? super V>> ConfigValue<V> defineInRange(String path, Supplier<V> defaultSupplier, V min, V max, Class<V> clazz) {
|
|
return defineInRange(split(path), defaultSupplier, min, max, clazz);
|
|
}
|
|
public <V extends Comparable<? super V>> ConfigValue<V> defineInRange(List<String> path, Supplier<V> defaultSupplier, V min, V max, Class<V> clazz) {
|
|
Range<V> range = new Range<>(clazz, min, max);
|
|
context.setRange(range);
|
|
if (min.compareTo(max) > 0)
|
|
throw new IllegalArgumentException("Range min most be less then max.");
|
|
return define(path, defaultSupplier, range);
|
|
}
|
|
public <T> ConfigValue<T> defineInList(String path, T defaultValue, Collection<? extends T> acceptableValues) {
|
|
return defineInList(split(path), defaultValue, acceptableValues);
|
|
}
|
|
public <T> ConfigValue<T> defineInList(String path, Supplier<T> defaultSupplier, Collection<? extends T> acceptableValues) {
|
|
return defineInList(split(path), defaultSupplier, acceptableValues);
|
|
}
|
|
public <T> ConfigValue<T> defineInList(List<String> path, T defaultValue, Collection<? extends T> acceptableValues) {
|
|
return defineInList(path, () -> defaultValue, acceptableValues);
|
|
}
|
|
public <T> ConfigValue<T> defineInList(List<String> path, Supplier<T> defaultSupplier, Collection<? extends T> acceptableValues) {
|
|
return define(path, defaultSupplier, acceptableValues::contains);
|
|
}
|
|
public <T> ConfigValue<List<? extends T>> defineList(String path, List<? extends T> defaultValue, Predicate<Object> elementValidator) {
|
|
return defineList(split(path), defaultValue, elementValidator);
|
|
}
|
|
public <T> ConfigValue<List<? extends T>> defineList(String path, Supplier<List<? extends T>> defaultSupplier, Predicate<Object> elementValidator) {
|
|
return defineList(split(path), defaultSupplier, elementValidator);
|
|
}
|
|
public <T> ConfigValue<List<? extends T>> defineList(List<String> path, List<? extends T> defaultValue, Predicate<Object> elementValidator) {
|
|
return defineList(path, () -> defaultValue, elementValidator);
|
|
}
|
|
public <T> ConfigValue<List<? extends T>> defineList(List<String> path, Supplier<List<? extends T>> defaultSupplier, Predicate<Object> elementValidator) {
|
|
context.setClazz(List.class);
|
|
return define(path, new ValueSpec(defaultSupplier, x -> x instanceof List && ((List<?>) x).stream().allMatch( elementValidator ), context) {
|
|
@Override
|
|
public Object correct(Object value) {
|
|
if (value == null || !(value instanceof List) || ((List<?>)value).isEmpty()) {
|
|
return getDefault();
|
|
}
|
|
List<?> list = Lists.newArrayList((List<?>) value);
|
|
list.removeIf(elementValidator.negate());
|
|
if (list.isEmpty()) {
|
|
return getDefault();
|
|
}
|
|
return list;
|
|
}
|
|
}, defaultSupplier);
|
|
}
|
|
|
|
//Enum
|
|
public <V extends Enum<V>> ConfigValue<V> defineEnum(String path, V defaultValue) {
|
|
return defineEnum(split(path), defaultValue);
|
|
}
|
|
public <V extends Enum<V>> ConfigValue<V> defineEnum(List<String> path, V defaultValue) {
|
|
return defineEnum(path, defaultValue, defaultValue.getDeclaringClass().getEnumConstants());
|
|
}
|
|
public <V extends Enum<V>> ConfigValue<V> defineEnum(String path, V defaultValue, @SuppressWarnings("unchecked") V... acceptableValues) {
|
|
return defineEnum(split(path), defaultValue, acceptableValues);
|
|
}
|
|
public <V extends Enum<V>> ConfigValue<V> defineEnum(List<String> path, V defaultValue, @SuppressWarnings("unchecked") V... acceptableValues) {
|
|
return defineEnum(path, defaultValue, Arrays.asList(acceptableValues));
|
|
}
|
|
public <V extends Enum<V>> ConfigValue<V> defineEnum(String path, V defaultValue, Collection<V> acceptableValues) {
|
|
return defineEnum(split(path), defaultValue, acceptableValues);
|
|
}
|
|
public <V extends Enum<V>> ConfigValue<V> defineEnum(List<String> path, V defaultValue, Collection<V> acceptableValues) {
|
|
return defineEnum(path, defaultValue, acceptableValues::contains);
|
|
}
|
|
public <V extends Enum<V>> ConfigValue<V> defineEnum(String path, V defaultValue, Predicate<Object> validator) {
|
|
return defineEnum(split(path), defaultValue, validator);
|
|
}
|
|
public <V extends Enum<V>> ConfigValue<V> defineEnum(List<String> path, V defaultValue, Predicate<Object> validator) {
|
|
return defineEnum(path, () -> defaultValue, validator, defaultValue.getDeclaringClass());
|
|
}
|
|
public <V extends Enum<V>> ConfigValue<V> defineEnum(String path, Supplier<V> defaultSupplier, Predicate<Object> validator, Class<V> clazz) {
|
|
return defineEnum(split(path), defaultSupplier, validator, clazz);
|
|
}
|
|
public <V extends Enum<V>> ConfigValue<V> defineEnum(List<String> path, Supplier<V> defaultSupplier, Predicate<Object> validator, Class<V> clazz) {
|
|
return define(path, defaultSupplier, validator, clazz);
|
|
}
|
|
|
|
|
|
//boolean
|
|
public BooleanValue define(String path, boolean defaultValue) {
|
|
return define(split(path), defaultValue);
|
|
}
|
|
public BooleanValue define(List<String> path, boolean defaultValue) {
|
|
return define(path, (Supplier<Boolean>)() -> defaultValue);
|
|
}
|
|
public BooleanValue define(String path, Supplier<Boolean> defaultSupplier) {
|
|
return define(split(path), defaultSupplier);
|
|
}
|
|
public BooleanValue define(List<String> path, Supplier<Boolean> defaultSupplier) {
|
|
return new BooleanValue(this, define(path, defaultSupplier, o -> {
|
|
if (o instanceof String) return ((String)o).equalsIgnoreCase("true") || ((String)o).equalsIgnoreCase("false");
|
|
return o instanceof Boolean;
|
|
}, Boolean.class).getPath(), defaultSupplier);
|
|
}
|
|
|
|
//Double
|
|
public DoubleValue defineInRange(String path, double defaultValue, double min, double max) {
|
|
return defineInRange(split(path), defaultValue, min, max);
|
|
}
|
|
public DoubleValue defineInRange(List<String> path, double defaultValue, double min, double max) {
|
|
return defineInRange(path, (Supplier<Double>)() -> defaultValue, min, max);
|
|
}
|
|
public DoubleValue defineInRange(String path, Supplier<Double> defaultSupplier, double min, double max) {
|
|
return defineInRange(split(path), defaultSupplier, min, max);
|
|
}
|
|
public DoubleValue defineInRange(List<String> path, Supplier<Double> defaultSupplier, double min, double max) {
|
|
return new DoubleValue(this, defineInRange(path, defaultSupplier, min, max, Double.class).getPath(), defaultSupplier);
|
|
}
|
|
|
|
//Ints
|
|
public IntValue defineInRange(String path, int defaultValue, int min, int max) {
|
|
return defineInRange(split(path), defaultValue, min, max);
|
|
}
|
|
public IntValue defineInRange(List<String> path, int defaultValue, int min, int max) {
|
|
return defineInRange(path, (Supplier<Integer>)() -> defaultValue, min, max);
|
|
}
|
|
public IntValue defineInRange(String path, Supplier<Integer> defaultSupplier, int min, int max) {
|
|
return defineInRange(split(path), defaultSupplier, min, max);
|
|
}
|
|
public IntValue defineInRange(List<String> path, Supplier<Integer> defaultSupplier, int min, int max) {
|
|
return new IntValue(this, defineInRange(path, defaultSupplier, min, max, Integer.class).getPath(), defaultSupplier);
|
|
}
|
|
|
|
//Longs
|
|
public LongValue defineInRange(String path, long defaultValue, long min, long max) {
|
|
return defineInRange(split(path), defaultValue, min, max);
|
|
}
|
|
public LongValue defineInRange(List<String> path, long defaultValue, long min, long max) {
|
|
return defineInRange(path, (Supplier<Long>)() -> defaultValue, min, max);
|
|
}
|
|
public LongValue defineInRange(String path, Supplier<Long> defaultSupplier, long min, long max) {
|
|
return defineInRange(split(path), defaultSupplier, min, max);
|
|
}
|
|
public LongValue defineInRange(List<String> path, Supplier<Long> defaultSupplier, long min, long max) {
|
|
return new LongValue(this, defineInRange(path, defaultSupplier, min, max, Long.class).getPath(), defaultSupplier);
|
|
}
|
|
|
|
public Builder comment(String comment)
|
|
{
|
|
context.setComment(comment);
|
|
return this;
|
|
}
|
|
public Builder comment(String... comment)
|
|
{
|
|
context.setComment(comment);
|
|
return this;
|
|
}
|
|
|
|
public Builder translation(String translationKey)
|
|
{
|
|
context.setTranslationKey(translationKey);
|
|
return this;
|
|
}
|
|
|
|
public Builder worldRestart()
|
|
{
|
|
context.worldRestart();
|
|
return this;
|
|
}
|
|
|
|
public Builder push(String path) {
|
|
return push(split(path));
|
|
}
|
|
|
|
public Builder push(List<String> path) {
|
|
currentPath.addAll(path);
|
|
if (context.getComment() != null) {
|
|
|
|
levelComments.put(new ArrayList<String>(currentPath), LINE_JOINER.join(context.getComment()));
|
|
context.setComment((String[])null);
|
|
}
|
|
context.ensureEmpty();
|
|
return this;
|
|
}
|
|
|
|
public Builder pop() {
|
|
return pop(1);
|
|
}
|
|
|
|
public Builder pop(int count) {
|
|
if (count > currentPath.size())
|
|
throw new IllegalArgumentException("Attempted to pop " + count + " elements when we only had: " + currentPath);
|
|
for (int x = 0; x < count; x++)
|
|
currentPath.remove(currentPath.size() - 1);
|
|
return this;
|
|
}
|
|
|
|
public <T> Pair<T, ForgeConfigSpec> configure(Function<Builder, T> consumer) {
|
|
T o = consumer.apply(this);
|
|
return Pair.of(o, this.build());
|
|
}
|
|
|
|
public ForgeConfigSpec build()
|
|
{
|
|
context.ensureEmpty();
|
|
ForgeConfigSpec ret = new ForgeConfigSpec(storage, levelComments);
|
|
values.forEach(v -> v.spec = ret);
|
|
return ret;
|
|
}
|
|
|
|
public interface BuilderConsumer {
|
|
void accept(Builder builder);
|
|
}
|
|
}
|
|
|
|
private static class BuilderContext
|
|
{
|
|
private String[] comment;
|
|
private String langKey;
|
|
private Range<?> range;
|
|
private boolean worldRestart = false;
|
|
private Class<?> clazz;
|
|
|
|
public void setComment(String... value) { this.comment = value; }
|
|
public String[] getComment() { return this.comment; }
|
|
public void setTranslationKey(String value) { this.langKey = value; }
|
|
public String getTranslationKey() { return this.langKey; }
|
|
public <V extends Comparable<? super V>> void setRange(Range<V> value)
|
|
{
|
|
this.range = value;
|
|
this.setClazz(value.getClazz());
|
|
}
|
|
@SuppressWarnings("unchecked")
|
|
public <V extends Comparable<? super V>> Range<V> getRange() { return (Range<V>)this.range; }
|
|
public void worldRestart() { this.worldRestart = true; }
|
|
public boolean needsWorldRestart() { return this.worldRestart; }
|
|
public void setClazz(Class<?> clazz) { this.clazz = clazz; }
|
|
public Class<?> getClazz(){ return this.clazz; }
|
|
|
|
public void ensureEmpty()
|
|
{
|
|
validate(comment, "Non-null comment when null expected");
|
|
validate(langKey, "Non-null translation key when null expected");
|
|
validate(range, "Non-null range when null expected");
|
|
validate(worldRestart, "Dangeling world restart value set to true");
|
|
}
|
|
|
|
private void validate(Object value, String message)
|
|
{
|
|
if (value != null)
|
|
throw new IllegalStateException(message);
|
|
}
|
|
private void validate(boolean value, String message)
|
|
{
|
|
if (value)
|
|
throw new IllegalStateException(message);
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unused")
|
|
private static class Range<V extends Comparable<? super V>> implements Predicate<Object>
|
|
{
|
|
private final Class<V> clazz;
|
|
private final V min;
|
|
private final V max;
|
|
|
|
private Range(Class<V> clazz, V min, V max)
|
|
{
|
|
this.clazz = clazz;
|
|
this.min = min;
|
|
this.max = max;
|
|
}
|
|
|
|
public Class<V> getClazz() { return clazz; }
|
|
public V getMin() { return min; }
|
|
public V getMax() { return max; }
|
|
|
|
@Override
|
|
public boolean test(Object t)
|
|
{
|
|
if (!clazz.isInstance(t)) return false;
|
|
V c = clazz.cast(t);
|
|
return c.compareTo(min) >= 0 && c.compareTo(max) <= 0;
|
|
}
|
|
}
|
|
|
|
public static class ValueSpec
|
|
{
|
|
private final String comment;
|
|
private final String langKey;
|
|
private final Range<?> range;
|
|
private final boolean worldRestart;
|
|
private final Class<?> clazz;
|
|
private final Supplier<?> supplier;
|
|
private final Predicate<Object> validator;
|
|
private Object _default = null;
|
|
|
|
private ValueSpec(Supplier<?> supplier, Predicate<Object> validator, BuilderContext context)
|
|
{
|
|
Objects.requireNonNull(supplier, "Default supplier can not be null");
|
|
Objects.requireNonNull(validator, "Validator can not be null");
|
|
|
|
this.comment = context.getComment() == null ? null : LINE_JOINER.join(context.getComment());
|
|
this.langKey = context.getTranslationKey();
|
|
this.range = context.getRange();
|
|
this.worldRestart = context.needsWorldRestart();
|
|
this.clazz = context.getClazz();
|
|
this.supplier = supplier;
|
|
this.validator = validator;
|
|
}
|
|
|
|
public String getComment() { return comment; }
|
|
public String getTranslationKey() { return langKey; }
|
|
@SuppressWarnings("unchecked")
|
|
public <V extends Comparable<? super V>> Range<V> getRange() { return (Range<V>)this.range; }
|
|
public boolean needsWorldRestart() { return this.worldRestart; }
|
|
public Class<?> getClazz(){ return this.clazz; }
|
|
public boolean test(Object value) { return validator.test(value); }
|
|
public Object correct(Object value) { return getDefault(); }
|
|
|
|
public Object getDefault()
|
|
{
|
|
if (_default == null)
|
|
_default = supplier.get();
|
|
return _default;
|
|
}
|
|
}
|
|
|
|
public static class ConfigValue<T>
|
|
{
|
|
private final Builder parent;
|
|
private final List<String> path;
|
|
private final Supplier<T> defaultSupplier;
|
|
|
|
private ForgeConfigSpec spec;
|
|
|
|
ConfigValue(Builder parent, List<String> path, Supplier<T> defaultSupplier)
|
|
{
|
|
this.parent = parent;
|
|
this.path = path;
|
|
this.defaultSupplier = defaultSupplier;
|
|
this.parent.values.add(this);
|
|
}
|
|
|
|
public List<String> getPath()
|
|
{
|
|
return Lists.newArrayList(path);
|
|
}
|
|
|
|
public T get()
|
|
{
|
|
Preconditions.checkNotNull(spec, "Cannot get config value before spec is built");
|
|
Preconditions.checkNotNull(spec.childConfig, "Cannot get config value without assigned Config object present");
|
|
return spec.childConfig.getOrElse(path, defaultSupplier);
|
|
}
|
|
|
|
public Builder next()
|
|
{
|
|
return parent;
|
|
}
|
|
}
|
|
|
|
public static class BooleanValue extends ConfigValue<Boolean>
|
|
{
|
|
BooleanValue(Builder parent, List<String> path, Supplier<Boolean> defaultSupplier)
|
|
{
|
|
super(parent, path, defaultSupplier);
|
|
}
|
|
}
|
|
|
|
public static class IntValue extends ConfigValue<Integer>
|
|
{
|
|
IntValue(Builder parent, List<String> path, Supplier<Integer> defaultSupplier)
|
|
{
|
|
super(parent, path, defaultSupplier);
|
|
}
|
|
}
|
|
|
|
public static class LongValue extends ConfigValue<Long>
|
|
{
|
|
LongValue(Builder parent, List<String> path, Supplier<Long> defaultSupplier)
|
|
{
|
|
super(parent, path, defaultSupplier);
|
|
}
|
|
}
|
|
|
|
public static class DoubleValue extends ConfigValue<Double>
|
|
{
|
|
DoubleValue(Builder parent, List<String> path, Supplier<Double> defaultSupplier)
|
|
{
|
|
super(parent, path, defaultSupplier);
|
|
}
|
|
}
|
|
|
|
private static final Joiner LINE_JOINER = Joiner.on("\n");
|
|
private static final Joiner DOT_JOINER = Joiner.on(".");
|
|
private static final Splitter DOT_SPLITTER = Splitter.on(".");
|
|
private static List<String> split(String path)
|
|
{
|
|
return Lists.newArrayList(DOT_SPLITTER.split(path));
|
|
}
|
|
}
|