mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2026-01-04 15:41:38 +00:00
改进模板系统
This commit is contained in:
@@ -132,6 +132,7 @@ warning.config.recipe.smithing_transform.post_processor.keep_component.missing_c
|
|||||||
warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is missing the required argument 'tags' for post-processors 'keep_tags'.</yellow>"
|
warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is missing the required argument 'tags' for post-processors 'keep_tags'.</yellow>"
|
||||||
warning.config.i18n.unknown_locale: "<yellow>Issue found in file <arg:0> - Unknown locale '<arg:1>'.</yellow>"
|
warning.config.i18n.unknown_locale: "<yellow>Issue found in file <arg:0> - Unknown locale '<arg:1>'.</yellow>"
|
||||||
warning.config.template.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated template '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
|
warning.config.template.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated template '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
|
||||||
|
warning.config.template.invalid: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid template '<arg:2>'.</yellow>"
|
||||||
warning.config.template.argument.self_increase_int.invalid_range: "<yellow>Issue found in file <arg:0> - The template '<arg:1>' is using a 'from' '<arg:2>' larger than 'to' '<arg:3>' in 'self_increase_int' argument.</yellow>"
|
warning.config.template.argument.self_increase_int.invalid_range: "<yellow>Issue found in file <arg:0> - The template '<arg:1>' is using a 'from' '<arg:2>' larger than 'to' '<arg:3>' in 'self_increase_int' argument.</yellow>"
|
||||||
warning.config.template.argument.list.invalid_type: "<yellow>Issue found in file <arg:0> - The template '<arg:1>' is using a 'list' argument which expects a 'List' as argument while the input argument is a(n) '<arg:2>'.</yellow>"
|
warning.config.template.argument.list.invalid_type: "<yellow>Issue found in file <arg:0> - The template '<arg:1>' is using a 'list' argument which expects a 'List' as argument while the input argument is a(n) '<arg:2>'.</yellow>"
|
||||||
warning.config.vanilla_loot.missing_type: "<yellow>Issue found in file <arg:0> - The vanilla loot '<arg:1>' is missing the required 'type' argument.</yellow>"
|
warning.config.vanilla_loot.missing_type: "<yellow>Issue found in file <arg:0> - The vanilla loot '<arg:1>' is missing the required 'type' argument.</yellow>"
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ warning.config.recipe.smithing_transform.post_processor.keep_component.missing_t
|
|||||||
warning.config.i18n.unknown_locale: "<yellow>在文件 <arg:0> 发现问题 - 未知的语言环境 '<arg:1>'</yellow>"
|
warning.config.i18n.unknown_locale: "<yellow>在文件 <arg:0> 发现问题 - 未知的语言环境 '<arg:1>'</yellow>"
|
||||||
warning.config.template.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的模板 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
|
warning.config.template.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的模板 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
|
||||||
warning.config.template.argument.self_increase_int.invalid_range: "<yellow>在文件 <arg:0> 发现问题 - 模板 '<arg:1>' 在 'self_increase_int' 参数中使用了一个起始值 '<arg:2>' 大于终止值 '<arg:3>'</yellow>"
|
warning.config.template.argument.self_increase_int.invalid_range: "<yellow>在文件 <arg:0> 发现问题 - 模板 '<arg:1>' 在 'self_increase_int' 参数中使用了一个起始值 '<arg:2>' 大于终止值 '<arg:3>'</yellow>"
|
||||||
|
warning.config.template.invalid: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 使用了无效的模板 '<arg:2>'.</yellow>"
|
||||||
warning.config.template.argument.list.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 模板 '<arg:1>' 的 'list' 参数需要列表类型 但输入参数类型为 '<arg:2>'</yellow>"
|
warning.config.template.argument.list.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 模板 '<arg:1>' 的 'list' 参数需要列表类型 但输入参数类型为 '<arg:2>'</yellow>"
|
||||||
warning.config.vanilla_loot.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 原版战利品 '<arg:1>' 缺少必需的 'type' 参数</yellow>"
|
warning.config.vanilla_loot.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 原版战利品 '<arg:1>' 缺少必需的 'type' 参数</yellow>"
|
||||||
warning.config.vanilla_loot.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 原版战利品 '<arg:1>' 使用了无效类型 '<arg:2>' 允许的类型: [<arg:3>]</yellow>"
|
warning.config.vanilla_loot.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 原版战利品 '<arg:1>' 使用了无效类型 '<arg:2>' 允许的类型: [<arg:3>]</yellow>"
|
||||||
|
|||||||
@@ -467,7 +467,8 @@ public abstract class AbstractPackManager implements PackManager {
|
|||||||
}
|
}
|
||||||
for (Map.Entry<String, Object> entry : cachedFile.config().entrySet()) {
|
for (Map.Entry<String, Object> entry : cachedFile.config().entrySet()) {
|
||||||
processConfigEntry(entry, path, cachedFile.pack(), (p, c) ->
|
processConfigEntry(entry, path, cachedFile.pack(), (p, c) ->
|
||||||
cachedConfigs.computeIfAbsent(p, k -> new ArrayList<>()).add(c));
|
cachedConfigs.computeIfAbsent(p, k -> new ArrayList<>()).add(c)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return FileVisitResult.CONTINUE;
|
return FileVisitResult.CONTINUE;
|
||||||
@@ -494,12 +495,13 @@ public abstract class AbstractPackManager implements PackManager {
|
|||||||
Key id = Key.withDefaultNamespace(key, cached.pack().namespace());
|
Key id = Key.withDefaultNamespace(key, cached.pack().namespace());
|
||||||
try {
|
try {
|
||||||
if (parser.supportsParsingObject()) {
|
if (parser.supportsParsingObject()) {
|
||||||
|
// do not apply templates
|
||||||
parser.parseObject(cached.pack(), cached.filePath(), id, configEntry.getValue());
|
parser.parseObject(cached.pack(), cached.filePath(), id, configEntry.getValue());
|
||||||
} else if (predicate.test(parser)) {
|
} else if (predicate.test(parser)) {
|
||||||
if (configEntry.getValue() instanceof Map<?, ?> configSection0) {
|
if (configEntry.getValue() instanceof Map<?, ?> configSection0) {
|
||||||
Map<String, Object> configSection1 = castToMap(configSection0, false);
|
Map<String, Object> config = castToMap(configSection0, false);
|
||||||
if ((boolean) configSection1.getOrDefault("enable", true)) {
|
if ((boolean) config.getOrDefault("enable", true)) {
|
||||||
parser.parseSection(cached.pack(), cached.filePath(), id, plugin.templateManager().applyTemplates(id, configSection1));
|
parser.parseSection(cached.pack(), cached.filePath(), id, MiscUtils.castToMap(this.plugin.templateManager().applyTemplates(id, config), false));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TranslationManager.instance().log("warning.config.structure.not_section", cached.filePath().toString(), cached.prefix() + "." + key, configEntry.getValue().getClass().getSimpleName());
|
TranslationManager.instance().log("warning.config.structure.not_section", cached.filePath().toString(), cached.prefix() + "." + key, configEntry.getValue().getClass().getSimpleName());
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package net.momirealms.craftengine.core.plugin.config.template;
|
||||||
|
|
||||||
|
import com.ezylang.evalex.Expression;
|
||||||
|
import com.ezylang.evalex.data.EvaluationValue;
|
||||||
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class ExpressionTemplateArgument implements TemplateArgument {
|
||||||
|
public static final Factory FACTORY = new Factory();
|
||||||
|
private final TemplateManager.ArgumentString expression;
|
||||||
|
private final ValueType valueType;
|
||||||
|
|
||||||
|
protected ExpressionTemplateArgument(String expression, ValueType valueType) {
|
||||||
|
this.expression = TemplateManager.preParse(expression);
|
||||||
|
this.valueType = valueType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(Map<String, TemplateArgument> arguments) {
|
||||||
|
String expression = Optional.ofNullable(this.expression.get(arguments)).map(String::valueOf).orElse(null);
|
||||||
|
if (expression == null) return null;
|
||||||
|
try {
|
||||||
|
return this.valueType.formatter().apply(new Expression(expression).evaluate());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to process expression argument: " + this.expression, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Key type() {
|
||||||
|
return TemplateArguments.EXPRESSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected enum ValueType {
|
||||||
|
INT(e -> e.getNumberValue().intValue()),
|
||||||
|
LONG(e -> e.getNumberValue().longValue()),
|
||||||
|
SHORT(e -> e.getNumberValue().shortValueExact()),
|
||||||
|
DOUBLE(e -> e.getNumberValue().doubleValue()),
|
||||||
|
FLOAT(e -> e.getNumberValue().floatValue()),
|
||||||
|
BOOLEAN(EvaluationValue::getBooleanValue),;
|
||||||
|
|
||||||
|
private final Function<EvaluationValue, Object> formatter;
|
||||||
|
|
||||||
|
ValueType(Function<EvaluationValue, Object> formatter) {
|
||||||
|
this.formatter = formatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Function<EvaluationValue, Object> formatter() {
|
||||||
|
return formatter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements TemplateArgumentFactory {
|
||||||
|
@Override
|
||||||
|
public TemplateArgument create(Map<String, Object> arguments) {
|
||||||
|
return new ExpressionTemplateArgument(
|
||||||
|
arguments.getOrDefault("expression", "").toString(),
|
||||||
|
ValueType.valueOf(arguments.getOrDefault("value-type", "double").toString().toUpperCase(Locale.ENGLISH))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ public class ListTemplateArgument implements TemplateArgument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Object> get() {
|
public List<Object> get(Map<String, TemplateArgument> arguments) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public class MapTemplateArgument implements TemplateArgument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> get() {
|
public Map<String, Object> get(Map<String, TemplateArgument> arguments) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public class NullTemplateArgument implements TemplateArgument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object get() {
|
public Object get(Map<String, TemplateArgument> arguments) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package net.momirealms.craftengine.core.plugin.config.template;
|
|||||||
|
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class ObjectTemplateArgument implements TemplateArgument {
|
public class ObjectTemplateArgument implements TemplateArgument {
|
||||||
private final Object value;
|
private final Object value;
|
||||||
|
|
||||||
@@ -9,13 +11,17 @@ public class ObjectTemplateArgument implements TemplateArgument {
|
|||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ObjectTemplateArgument of(Object value) {
|
||||||
|
return new ObjectTemplateArgument(value);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Key type() {
|
public Key type() {
|
||||||
return TemplateArguments.OBJECT;
|
return TemplateArguments.OBJECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object get() {
|
public Object get(Map<String, TemplateArgument> arguments) {
|
||||||
return this.value;
|
return this.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public class PlainStringTemplateArgument implements TemplateArgument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String get() {
|
public String get(Map<String, TemplateArgument> arguments) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class SelfIncreaseIntTemplateArgument implements TemplateArgument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String get() {
|
public String get(Map<String, TemplateArgument> arguments) {
|
||||||
String value = String.valueOf(this.current);
|
String value = String.valueOf(this.current);
|
||||||
if (this.current < this.max) this.current += 1;
|
if (this.current < this.max) this.current += 1;
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package net.momirealms.craftengine.core.plugin.config.template;
|
|||||||
|
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.Map;
|
||||||
|
|
||||||
public interface TemplateArgument extends Supplier<Object> {
|
public interface TemplateArgument {
|
||||||
|
|
||||||
Key type();
|
Key type();
|
||||||
|
|
||||||
|
Object get(Map<String, TemplateArgument> arguments);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ public class TemplateArguments {
|
|||||||
public static final Key MAP = Key.of("craftengine:map");
|
public static final Key MAP = Key.of("craftengine:map");
|
||||||
public static final Key LIST = Key.of("craftengine:list");
|
public static final Key LIST = Key.of("craftengine:list");
|
||||||
public static final Key NULL = Key.of("craftengine:null");
|
public static final Key NULL = Key.of("craftengine:null");
|
||||||
|
public static final Key EXPRESSION = Key.of("craftengine:expression");
|
||||||
public static final Key OBJECT = Key.of("craftengine:object"); // No Factory, internal use
|
public static final Key OBJECT = Key.of("craftengine:object"); // No Factory, internal use
|
||||||
|
|
||||||
public static void register(Key key, TemplateArgumentFactory factory) {
|
public static void register(Key key, TemplateArgumentFactory factory) {
|
||||||
@@ -29,6 +30,7 @@ public class TemplateArguments {
|
|||||||
register(MAP, MapTemplateArgument.FACTORY);
|
register(MAP, MapTemplateArgument.FACTORY);
|
||||||
register(LIST, ListTemplateArgument.FACTORY);
|
register(LIST, ListTemplateArgument.FACTORY);
|
||||||
register(NULL, NullTemplateArgument.FACTORY);
|
register(NULL, NullTemplateArgument.FACTORY);
|
||||||
|
register(EXPRESSION, ExpressionTemplateArgument.FACTORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TemplateArgument fromMap(Map<String, Object> map) {
|
public static TemplateArgument fromMap(Map<String, Object> map) {
|
||||||
|
|||||||
@@ -4,19 +4,274 @@ import net.momirealms.craftengine.core.plugin.Manageable;
|
|||||||
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
|
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public interface TemplateManager extends Manageable {
|
public interface TemplateManager extends Manageable {
|
||||||
Pattern ARGUMENT_PATTERN = Pattern.compile("\\{[^{}]+}");
|
|
||||||
String LEFT_BRACKET = "{";
|
|
||||||
String RIGHT_BRACKET = "}";
|
|
||||||
String TEMPLATE = "template";
|
|
||||||
String OVERRIDES = "overrides";
|
|
||||||
String ARGUMENTS = "arguments";
|
|
||||||
String MERGES = "merges";
|
|
||||||
|
|
||||||
ConfigParser parser();
|
ConfigParser parser();
|
||||||
|
|
||||||
Map<String, Object> applyTemplates(Key id, Map<String, Object> input);
|
Object applyTemplates(Key id, Object input);
|
||||||
|
|
||||||
|
interface ArgumentString {
|
||||||
|
|
||||||
|
String rawValue();
|
||||||
|
|
||||||
|
Object get(Map<String, TemplateArgument> arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
final class Literal implements ArgumentString {
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
public Literal(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Literal literal(String value) {
|
||||||
|
return new Literal(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String rawValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(Map<String, TemplateArgument> arguments) {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof Literal literal)) return false;
|
||||||
|
return this.value.equals(literal.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.value.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Literal(" + this.value + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class Placeholder implements ArgumentString {
|
||||||
|
private final String placeholder;
|
||||||
|
private final String rawText;
|
||||||
|
|
||||||
|
public Placeholder(String placeholder) {
|
||||||
|
this.placeholder = placeholder;
|
||||||
|
this.rawText = "{" + this.placeholder + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Placeholder placeholder(String placeholder) {
|
||||||
|
return new Placeholder(placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(Map<String, TemplateArgument> arguments) {
|
||||||
|
TemplateArgument replacement = arguments.get(this.placeholder);
|
||||||
|
if (replacement != null) {
|
||||||
|
return replacement.get(arguments);
|
||||||
|
}
|
||||||
|
return rawValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String rawValue() {
|
||||||
|
return this.rawText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof Placeholder that)) return false;
|
||||||
|
return this.placeholder.equals(that.placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.placeholder.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Placeholder(" + this.placeholder + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class Complex2 implements ArgumentString {
|
||||||
|
private final String rawText;
|
||||||
|
private final ArgumentString arg1;
|
||||||
|
private final ArgumentString arg2;
|
||||||
|
|
||||||
|
public Complex2(String rawText, ArgumentString arg1, ArgumentString arg2) {
|
||||||
|
this.arg1 = arg1;
|
||||||
|
this.arg2 = arg2;
|
||||||
|
this.rawText = rawText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(Map<String, TemplateArgument> arguments) {
|
||||||
|
Object arg1 = this.arg1.get(arguments);
|
||||||
|
Object arg2 = this.arg2.get(arguments);
|
||||||
|
if (arg1 == null && arg2 == null) return null;
|
||||||
|
if (arg1 == null) return String.valueOf(arg2);
|
||||||
|
if (arg2 == null) return String.valueOf(arg1);
|
||||||
|
return String.valueOf(arg1) + arg2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String rawValue() {
|
||||||
|
return this.rawText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof Complex that)) return false;
|
||||||
|
return this.rawText.equals(that.rawText);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.rawText.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Complex2(" + this.rawText + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class Complex implements ArgumentString {
|
||||||
|
private final List<ArgumentString> parts;
|
||||||
|
private final String rawText;
|
||||||
|
|
||||||
|
public Complex(String rawText, List<ArgumentString> parts) {
|
||||||
|
this.parts = parts;
|
||||||
|
this.rawText = rawText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(Map<String, TemplateArgument> arguments) {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
boolean hasValue = false;
|
||||||
|
for (ArgumentString part : this.parts) {
|
||||||
|
Object arg = part.get(arguments);
|
||||||
|
if (arg != null) {
|
||||||
|
result.append(arg);
|
||||||
|
hasValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasValue) return null;
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String rawValue() {
|
||||||
|
return this.rawText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof Complex that)) return false;
|
||||||
|
return this.rawText.equals(that.rawText);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.rawText.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Complex(" + this.rawText + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ArgumentString preParse(String input) {
|
||||||
|
if (input == null || input.isEmpty()) {
|
||||||
|
return Literal.literal("");
|
||||||
|
}
|
||||||
|
int n = input.length();
|
||||||
|
int lastAppendPosition = 0; // 追踪上一次追加操作结束的位置
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
List<ArgumentString> arguments = new ArrayList<>();
|
||||||
|
while (i < n) {
|
||||||
|
// 检查当前字符是否为未转义的 '{'
|
||||||
|
int backslashes = 0;
|
||||||
|
int temp_i = i - 1;
|
||||||
|
while (temp_i >= 0 && input.charAt(temp_i) == '\\') {
|
||||||
|
backslashes++;
|
||||||
|
temp_i--;
|
||||||
|
}
|
||||||
|
if (input.charAt(i) == '{' && backslashes % 2 == 0) {
|
||||||
|
// 发现占位符起点
|
||||||
|
int placeholderStartIndex = i;
|
||||||
|
// 追加从上一个位置到当前占位符之前的文本
|
||||||
|
if (lastAppendPosition < i) {
|
||||||
|
arguments.add(Literal.literal(input.substring(lastAppendPosition, i)));
|
||||||
|
}
|
||||||
|
// --- 开始解析占位符内部 ---
|
||||||
|
StringBuilder keyBuilder = new StringBuilder();
|
||||||
|
int depth = 1;
|
||||||
|
int j = i + 1;
|
||||||
|
boolean foundMatch = false;
|
||||||
|
while (j < n) {
|
||||||
|
char c = input.charAt(j);
|
||||||
|
if (c == '\\') { // 处理转义
|
||||||
|
if (j + 1 < n) {
|
||||||
|
keyBuilder.append(input.charAt(j + 1));
|
||||||
|
j += 2;
|
||||||
|
} else {
|
||||||
|
keyBuilder.append(c);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
} else if (c == '{') {
|
||||||
|
depth++;
|
||||||
|
keyBuilder.append(c);
|
||||||
|
j++;
|
||||||
|
} else if (c == '}') {
|
||||||
|
depth--;
|
||||||
|
if (depth == 0) { // 找到匹配的结束括号
|
||||||
|
String key = keyBuilder.toString();
|
||||||
|
arguments.add(Placeholder.placeholder(key));
|
||||||
|
|
||||||
|
// 更新位置指针
|
||||||
|
i = j + 1;
|
||||||
|
lastAppendPosition = i;
|
||||||
|
foundMatch = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
keyBuilder.append(c); // 嵌套的 '}'
|
||||||
|
j++;
|
||||||
|
} else {
|
||||||
|
keyBuilder.append(c);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --- 占位符解析结束 ---
|
||||||
|
if (!foundMatch) {
|
||||||
|
// 如果内层循环结束仍未找到匹配的 '}',则不进行任何特殊处理
|
||||||
|
// 外层循环的 i 会自然递增
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 追加最后一个占位符之后的所有剩余文本
|
||||||
|
if (lastAppendPosition < n) {
|
||||||
|
arguments.add(Literal.literal(input.substring(lastAppendPosition)));
|
||||||
|
}
|
||||||
|
return switch (arguments.size()) {
|
||||||
|
case 1 -> arguments.getFirst();
|
||||||
|
case 2 -> new Complex2(input, arguments.get(0), arguments.get(1));
|
||||||
|
default -> new Complex(input, arguments);
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,30 +4,38 @@ import net.momirealms.craftengine.core.pack.LoadingSequence;
|
|||||||
import net.momirealms.craftengine.core.pack.Pack;
|
import net.momirealms.craftengine.core.pack.Pack;
|
||||||
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
|
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
|
||||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||||
import net.momirealms.craftengine.core.util.GsonHelper;
|
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
|
|
||||||
@SuppressWarnings("DuplicatedCode")
|
@SuppressWarnings("DuplicatedCode")
|
||||||
public class TemplateManagerImpl implements TemplateManager {
|
public class TemplateManagerImpl implements TemplateManager {
|
||||||
/*
|
private static final ArgumentString TEMPLATE = Literal.literal("template");
|
||||||
* 此类仍需要一次重构,对模板预解析,避免每次调用时候重新判断值是否含有参数
|
private static final ArgumentString OVERRIDES = Literal.literal("overrides");
|
||||||
*/
|
private static final ArgumentString ARGUMENTS = Literal.literal("arguments");
|
||||||
|
private static final ArgumentString MERGES = Literal.literal("merges");
|
||||||
|
private final static Set<ArgumentString> NON_TEMPLATE_ARGUMENTS = new HashSet<>(Set.of(TEMPLATE, ARGUMENTS, OVERRIDES, MERGES));
|
||||||
|
|
||||||
private final Map<Key, Object> templates = new HashMap<>();
|
private final Map<Key, Object> templates = new HashMap<>();
|
||||||
private final static Set<String> NON_TEMPLATE_KEY = new HashSet<>(Set.of(TEMPLATE, ARGUMENTS, OVERRIDES, MERGES));
|
|
||||||
private final TemplateParser templateParser;
|
private final TemplateParser templateParser;
|
||||||
|
|
||||||
public TemplateManagerImpl() {
|
public TemplateManagerImpl() {
|
||||||
this.templateParser = new TemplateParser();
|
this.templateParser = new TemplateParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unload() {
|
||||||
|
this.templates.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigParser parser() {
|
||||||
|
return this.templateParser;
|
||||||
|
}
|
||||||
|
|
||||||
public class TemplateParser implements ConfigParser {
|
public class TemplateParser implements ConfigParser {
|
||||||
public static final String[] CONFIG_SECTION_NAME = new String[] {"templates", "template"};
|
public static final String[] CONFIG_SECTION_NAME = new String[] {"templates", "template"};
|
||||||
|
|
||||||
@@ -51,413 +59,270 @@ public class TemplateManagerImpl implements TemplateManager {
|
|||||||
if (templates.containsKey(id)) {
|
if (templates.containsKey(id)) {
|
||||||
throw new LocalizedResourceConfigException("warning.config.template.duplicate", path.toString(), id.toString());
|
throw new LocalizedResourceConfigException("warning.config.template.duplicate", path.toString(), id.toString());
|
||||||
}
|
}
|
||||||
templates.put(id, obj);
|
// 预处理会将 string类型的键或值解析为ArgumentString,以加速模板应用。所以处理后不可能存在String类型。
|
||||||
|
templates.put(id, preprocessUnknownValue(obj));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unload() {
|
public Object applyTemplates(Key id, Object input) {
|
||||||
this.templates.clear();
|
Object preprocessedInput = preprocessUnknownValue(input);
|
||||||
|
return processUnknownValue(preprocessedInput, Map.of(
|
||||||
|
"__NAMESPACE__", PlainStringTemplateArgument.plain(id.namespace()),
|
||||||
|
"__ID__", PlainStringTemplateArgument.plain(id.value())
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private Object preprocessUnknownValue(Object value) {
|
||||||
public ConfigParser parser() {
|
switch (value) {
|
||||||
return this.templateParser;
|
case Map<?, ?> map -> {
|
||||||
}
|
Map<String, Object> in = MiscUtils.castToMap(map, false);
|
||||||
|
Map<ArgumentString, Object> out = new LinkedHashMap<>(map.size());
|
||||||
@Override
|
for (Map.Entry<String, Object> entry : in.entrySet()) {
|
||||||
public Map<String, Object> applyTemplates(Key id, Map<String, Object> input) {
|
out.put(TemplateManager.preParse(entry.getKey()), preprocessUnknownValue(entry.getValue()));
|
||||||
Objects.requireNonNull(input, "Input must not be null");
|
}
|
||||||
Map<String, Object> result = new LinkedHashMap<>();
|
return out;
|
||||||
processMap(input,
|
}
|
||||||
Map.of("__ID__", PlainStringTemplateArgument.plain(id.value()),
|
case List<?> list -> {
|
||||||
"__NAMESPACE__", PlainStringTemplateArgument.plain(id.namespace())),
|
List<Object> objList = new ArrayList<>(list.size());
|
||||||
(obj) -> {
|
for (Object o : list) {
|
||||||
// 当前位于根节点下,如果下一级就是模板,则应把模板结果与当前map合并
|
objList.add(preprocessUnknownValue(o));
|
||||||
// 如果模板结果不是map,则为非法值,因为不可能出现类似于下方的配置
|
}
|
||||||
// items:
|
return objList;
|
||||||
// test:invalid: 111
|
}
|
||||||
if (obj instanceof Map<?,?> mapResult) {
|
case String string -> {
|
||||||
result.putAll(MiscUtils.castToMap(mapResult, false));
|
return TemplateManager.preParse(string);
|
||||||
} else {
|
}
|
||||||
throw new IllegalArgumentException("Invalid template used. Input: " + GsonHelper.get().toJson(input) + ". Template: " + GsonHelper.get().toJson(obj));
|
case null, default -> {
|
||||||
}
|
return value;
|
||||||
});
|
}
|
||||||
return result;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对于处理map,只有input是已知map,而返回值可能并不是
|
// 对于处理map,只有input是已知map,而返回值可能并不是
|
||||||
private void processMap(Map<String, Object> input,
|
private Object processMap(Map<ArgumentString, Object> input,
|
||||||
Map<String, TemplateArgument> parentArguments,
|
Map<String, TemplateArgument> arguments) {
|
||||||
// 只有当前为模板的时候,才会调用callback
|
|
||||||
Consumer<Object> processCallBack) {
|
|
||||||
// 传入的input是否含有template,这种情况下,返回值有可能是非map
|
// 传入的input是否含有template,这种情况下,返回值有可能是非map
|
||||||
if (input.containsKey(TEMPLATE)) {
|
if (input.containsKey(TEMPLATE)) {
|
||||||
TemplateProcessingResult processingResult = processTemplates(input, parentArguments);
|
TemplateProcessingResult processingResult = processTemplates(input, arguments);
|
||||||
List<Object> templates = processingResult.templates();
|
List<Object> processedTemplates = processingResult.templates();
|
||||||
// 你敢保证template里没有template吗?
|
if (!processedTemplates.isEmpty()) {
|
||||||
List<Object> processedTemplates = new ArrayList<>();
|
// 先获取第一个模板的类型
|
||||||
// 先递归处理后再合并
|
Object firstTemplate = processedTemplates.getFirst();
|
||||||
for (Object template : templates) {
|
// 如果是map,应当深度合并
|
||||||
processUnknownTypeMember(template, processingResult.arguments(), processedTemplates::add);
|
if (firstTemplate instanceof Map<?,?>) {
|
||||||
}
|
Map<String, Object> results = new LinkedHashMap<>();
|
||||||
if (processedTemplates.isEmpty()) {
|
for (Object processedTemplate : processedTemplates) {
|
||||||
return;
|
if (processedTemplate instanceof Map<?, ?> map) {
|
||||||
}
|
deepMergeMaps(results, MiscUtils.castToMap(map, false));
|
||||||
Object firstTemplate = processedTemplates.get(0);
|
}
|
||||||
// 如果是map,应当深度合并
|
|
||||||
if (firstTemplate instanceof Map<?,?>) {
|
|
||||||
Map<String, Object> results = new LinkedHashMap<>();
|
|
||||||
for (Object processedTemplate : processedTemplates) {
|
|
||||||
if (processedTemplate instanceof Map<?, ?> anotherMap) {
|
|
||||||
deepMergeMaps(results, MiscUtils.castToMap(anotherMap, false));
|
|
||||||
}
|
}
|
||||||
}
|
if (processingResult.overrides() instanceof Map<?, ?> overrides) {
|
||||||
if (processingResult.overrides() instanceof Map<?, ?> overrides) {
|
results.putAll(MiscUtils.castToMap(overrides, false));
|
||||||
results.putAll(MiscUtils.castToMap(overrides, false));
|
|
||||||
}
|
|
||||||
if (processingResult.merges() instanceof Map<?, ?> merges) {
|
|
||||||
deepMergeMaps(results, MiscUtils.castToMap(merges, false));
|
|
||||||
}
|
|
||||||
processCallBack.accept(results);
|
|
||||||
} else if (firstTemplate instanceof List<?>) {
|
|
||||||
List<Object> results = new ArrayList<>();
|
|
||||||
// 仅仅合并list
|
|
||||||
for (Object processedTemplate : processedTemplates) {
|
|
||||||
if (processedTemplate instanceof List<?> anotherList) {
|
|
||||||
results.addAll(anotherList);
|
|
||||||
}
|
}
|
||||||
}
|
if (processingResult.merges() instanceof Map<?, ?> merges) {
|
||||||
if (processingResult.overrides() instanceof List<?> overrides) {
|
deepMergeMaps(results, MiscUtils.castToMap(merges, false));
|
||||||
results.clear();
|
}
|
||||||
results.addAll(overrides);
|
return results;
|
||||||
}
|
} else if (firstTemplate instanceof List<?>) {
|
||||||
if (processingResult.merges() instanceof List<?> merges) {
|
List<Object> results = new ArrayList<>();
|
||||||
results.addAll(merges);
|
// 仅仅合并list
|
||||||
}
|
for (Object processedTemplate : processedTemplates) {
|
||||||
processCallBack.accept(results);
|
if (processedTemplate instanceof List<?> anotherList) {
|
||||||
} else {
|
results.addAll(anotherList);
|
||||||
Object overrides = processingResult.overrides();
|
}
|
||||||
if (overrides != null) {
|
}
|
||||||
processCallBack.accept(overrides);
|
if (processingResult.overrides() instanceof List<?> overrides) {
|
||||||
|
results.clear();
|
||||||
|
results.addAll(overrides);
|
||||||
|
}
|
||||||
|
if (processingResult.merges() instanceof List<?> merges) {
|
||||||
|
results.addAll(merges);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
} else {
|
} else {
|
||||||
// 其他情况下应当忽略其他的template
|
// 有覆写用覆写,无覆写返回最后一个模板值
|
||||||
processCallBack.accept(firstTemplate);
|
if (processingResult.overrides() != null) {
|
||||||
|
return processingResult.overrides();
|
||||||
|
}
|
||||||
|
if (processingResult.merges() != null) {
|
||||||
|
return processingResult.merges();
|
||||||
|
}
|
||||||
|
return processedTemplates.getLast();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// 模板为空啦,如果是map,则合并
|
||||||
|
if (processingResult.overrides() instanceof Map<?,?> overrides) {
|
||||||
|
Map<String, Object> output = new LinkedHashMap<>(MiscUtils.castToMap(overrides, false));
|
||||||
|
if (processingResult.merges() instanceof Map<?,?> merges) {
|
||||||
|
deepMergeMaps(output, MiscUtils.castToMap(merges, false));
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
} else if (processingResult.overrides() instanceof List<?> overrides) {
|
||||||
|
List<Object> output = new ArrayList<>(overrides);
|
||||||
|
if (processingResult.merges() instanceof List<?> merges) {
|
||||||
|
output.addAll(merges);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
// 否则有overrides就返回overrides
|
||||||
|
if (processingResult.overrides() != null) {
|
||||||
|
return processingResult.overrides();
|
||||||
|
}
|
||||||
|
// 否则有merges就返回merges
|
||||||
|
if (processingResult.merges() != null) {
|
||||||
|
return processingResult.merges();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 如果不是模板,则返回值一定是map
|
// 如果不是模板,则返回值一定是map
|
||||||
// 依次处理map下的每个参数
|
// 依次处理map下的每个参数
|
||||||
Map<String, Object> result = new LinkedHashMap<>();
|
Map<String, Object> result = new LinkedHashMap<>(input.size());
|
||||||
for (Map.Entry<String, Object> inputEntry : input.entrySet()) {
|
for (Map.Entry<ArgumentString, Object> inputEntry : input.entrySet()) {
|
||||||
String key = applyArgument(inputEntry.getKey(), parentArguments).toString();
|
Object key = inputEntry.getKey().get(arguments);
|
||||||
processUnknownTypeMember(inputEntry.getValue(), parentArguments, (processed) -> result.put(key, processed));
|
// 如果key为null说明不插入此键
|
||||||
|
if (key != null) {
|
||||||
|
result.put(key.toString(), processUnknownValue(inputEntry.getValue(), arguments));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
processCallBack.accept(result);
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理一个类型未知的值,本方法只管将member处理好后,传递回调用者
|
// 处理一个类型未知的值,本方法只管将member处理好后,传递回调用者a
|
||||||
private void processUnknownTypeMember(Object member,
|
@SuppressWarnings("unchecked")
|
||||||
Map<String, TemplateArgument> parentArguments,
|
private Object processUnknownValue(Object value,
|
||||||
Consumer<Object> processCallback) {
|
Map<String, TemplateArgument> arguments) {
|
||||||
if (member instanceof Map<?,?> innerMap) {
|
switch (value) {
|
||||||
|
case Map<?, ?> innerMap ->
|
||||||
// map下面还是个map吗?这并不一定
|
// map下面还是个map吗?这并不一定
|
||||||
// 比如
|
// 这时候并不一定是map,最终类型取决于template,那么应当根据template的结果进行调整,所以我们继续交给上方方法处理
|
||||||
// a:
|
{
|
||||||
// template: xxx
|
return processMap((Map<ArgumentString, Object>) innerMap, arguments);
|
||||||
// 这时候a并不一定是map,最终类型取决于template,那么应当根据template的结果进行调整,所以我们继续交给上方方法处理
|
}
|
||||||
processMap(MiscUtils.castToMap(innerMap, false), parentArguments, processCallback);
|
case List<?> innerList -> {
|
||||||
} else if (member instanceof List<?> innerList) {
|
List<Object> result = new ArrayList<>();
|
||||||
// map 下面是个list,那么对下面的每个成员再次处理
|
for (Object item : innerList) {
|
||||||
List<Object> result = new ArrayList<>();
|
result.add(processUnknownValue(item, arguments));
|
||||||
for (Object item : innerList) {
|
}
|
||||||
// 处理完以后,加入到list内
|
return result;
|
||||||
processUnknownTypeMember(item, parentArguments, result::add);
|
}
|
||||||
|
case ArgumentString arg -> {
|
||||||
|
return arg.get(arguments);
|
||||||
|
}
|
||||||
|
case null, default -> {
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
processCallback.accept(result);
|
|
||||||
} else if (member instanceof String possibleArgument) {
|
|
||||||
// 如果是个string,其可能是 {xxx} 的参数,那么就尝试应用参数后再返回
|
|
||||||
processCallback.accept(applyArgument(possibleArgument, parentArguments));
|
|
||||||
} else {
|
|
||||||
// 对于其他值,直接处理
|
|
||||||
processCallback.accept(member);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private TemplateProcessingResult processTemplates(Map<String, Object> input,
|
@SuppressWarnings("unchecked")
|
||||||
|
private TemplateProcessingResult processTemplates(Map<ArgumentString, Object> input,
|
||||||
Map<String, TemplateArgument> parentArguments) {
|
Map<String, TemplateArgument> parentArguments) {
|
||||||
|
int knownKeys = 1;
|
||||||
// 先获取template节点下所有的模板
|
// 先获取template节点下所有的模板
|
||||||
List<String> templateIds = MiscUtils.getAsStringList(input.get(TEMPLATE));
|
List<ArgumentString> templateIds = MiscUtils.getAsList(input.get(TEMPLATE), ArgumentString.class);
|
||||||
List<Object> templateList = new ArrayList<>(templateIds.size());
|
List<Object> templateList = new ArrayList<>(templateIds.size());
|
||||||
|
|
||||||
for (String templateId : templateIds) {
|
// 获取arguments
|
||||||
// 如果模板id被用了参数,则应先应用参数后再查询模板
|
|
||||||
Object actualTemplate = applyArgument(templateId, parentArguments);
|
|
||||||
if (actualTemplate == null) continue; // 忽略被null掉的模板
|
|
||||||
Object template = Optional.ofNullable(this.templates.get(Key.of(actualTemplate.toString())))
|
|
||||||
.orElseThrow(() -> new IllegalArgumentException("Template not found: " + actualTemplate));
|
|
||||||
templateList.add(template);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object argument = input.get(ARGUMENTS);
|
Object argument = input.get(ARGUMENTS);
|
||||||
boolean hasArgument = argument != null;
|
boolean hasArgument = argument != null;
|
||||||
|
if (hasArgument) knownKeys++;
|
||||||
|
|
||||||
// 将本节点下的参数与父参数合并
|
// 将本节点下的参数与父参数合并
|
||||||
Map<String, TemplateArgument> arguments = !hasArgument ? parentArguments : mergeArguments(
|
Map<String, TemplateArgument> arguments = hasArgument ? mergeArguments(
|
||||||
MiscUtils.castToMap(argument, false),
|
(Map<ArgumentString, Object>) argument,
|
||||||
parentArguments
|
parentArguments
|
||||||
);
|
) : parentArguments;
|
||||||
|
|
||||||
|
// 获取处理后的template
|
||||||
|
for (ArgumentString templateId : templateIds) {
|
||||||
|
// 如果模板id被用了参数,则应先应用参数后再查询模板
|
||||||
|
Object actualTemplate = templateId.get(parentArguments);
|
||||||
|
if (actualTemplate == null) continue; // 忽略被null掉的模板
|
||||||
|
Object template = Optional.ofNullable(this.templates.get(Key.of(actualTemplate.toString())))
|
||||||
|
.orElseThrow(() -> new LocalizedResourceConfigException("warning.config.template.invalid", actualTemplate.toString()));
|
||||||
|
Object processedTemplate = processUnknownValue(template, arguments);
|
||||||
|
if (processedTemplate != null) templateList.add(processedTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取overrides
|
||||||
Object override = input.get(OVERRIDES);
|
Object override = input.get(OVERRIDES);
|
||||||
if (override instanceof Map<?, ?> rawOverrides) {
|
boolean hasOverrides = override != null;
|
||||||
// 对overrides参数应用 本节点 + 父节点 参数
|
if (hasOverrides) {
|
||||||
Map<String, Object> overrides = new LinkedHashMap<>();
|
knownKeys++;
|
||||||
processMap(MiscUtils.castToMap(rawOverrides, false), arguments, (obj) -> {
|
override = processUnknownValue(override, arguments);
|
||||||
// 如果overrides的下一级就是一个模板,则模板必须为map类型
|
}
|
||||||
if (obj instanceof Map<?,?> mapResult) {
|
|
||||||
overrides.putAll(MiscUtils.castToMap(mapResult, false));
|
// 获取merges
|
||||||
} else {
|
Object merge = input.get(MERGES);
|
||||||
throw new IllegalArgumentException("Invalid template used. Input: " + GsonHelper.get().toJson(input) + ". Template: " + GsonHelper.get().toJson(obj));
|
boolean hasMerges = merge != null;
|
||||||
}
|
if (hasMerges) {
|
||||||
});
|
knownKeys++;
|
||||||
// overrides是map了,merges也只能是map
|
merge = processUnknownValue(merge, arguments);
|
||||||
if (input.get(MERGES) instanceof Map<?, ?> rawMerges) {
|
}
|
||||||
Map<String, Object> merges = new LinkedHashMap<>();
|
|
||||||
processMap(MiscUtils.castToMap(rawMerges, false), arguments, (obj) -> {
|
// 有其他意外参数
|
||||||
// 如果merges的下一级就是一个模板,则模板必须为map类型
|
if (input.size() > knownKeys) {
|
||||||
if (obj instanceof Map<?,?> mapResult) {
|
Map<String, Object> merges = new LinkedHashMap<>();
|
||||||
merges.putAll(MiscUtils.castToMap(mapResult, false));
|
// 会不会有一种可能,有笨比用户把模板和普通配置混合在了一起?再次遍历input后处理。
|
||||||
} else {
|
for (Map.Entry<ArgumentString, Object> inputEntry : input.entrySet()) {
|
||||||
throw new IllegalArgumentException("Invalid template used. Input: " + GsonHelper.get().toJson(input) + ". Template: " + GsonHelper.get().toJson(obj));
|
ArgumentString inputKey = inputEntry.getKey();
|
||||||
}
|
if (NON_TEMPLATE_ARGUMENTS.contains(inputKey)) continue;
|
||||||
});
|
Object key = inputKey.get(parentArguments);
|
||||||
// 已有template,merges,overrides 和可选的arguments
|
if (key != null) {
|
||||||
if (input.size() > (hasArgument ? 4 : 3)) {
|
merges.put(key.toString(), processUnknownValue(inputEntry.getValue(), arguments));
|
||||||
// 会不会有一种可能,有笨比用户把模板和普通配置混合在了一起?再次遍历input后处理
|
|
||||||
for (Map.Entry<String, Object> inputEntry : input.entrySet()) {
|
|
||||||
String inputKey = inputEntry.getKey();
|
|
||||||
if (NON_TEMPLATE_KEY.contains(inputKey)) continue;
|
|
||||||
processUnknownTypeMember(inputEntry.getValue(), arguments, (processed) -> merges.put(inputKey, processed));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 返回处理结果
|
|
||||||
return new TemplateProcessingResult(
|
|
||||||
templateList,
|
|
||||||
overrides,
|
|
||||||
merges,
|
|
||||||
arguments
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// 已有template,overrides 和可选的arguments
|
|
||||||
if (input.size() > (hasArgument ? 3 : 2)) {
|
|
||||||
Map<String, Object> merges = new LinkedHashMap<>();
|
|
||||||
// 会不会有一种可能,有笨比用户把模板和普通配置混合在了一起?再次遍历input后处理
|
|
||||||
for (Map.Entry<String, Object> inputEntry : input.entrySet()) {
|
|
||||||
String inputKey = inputEntry.getKey();
|
|
||||||
if (NON_TEMPLATE_KEY.contains(inputKey)) continue;
|
|
||||||
processUnknownTypeMember(inputEntry.getValue(), arguments, (processed) -> merges.put(inputKey, processed));
|
|
||||||
}
|
|
||||||
return new TemplateProcessingResult(
|
|
||||||
templateList,
|
|
||||||
overrides,
|
|
||||||
merges,
|
|
||||||
arguments
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return new TemplateProcessingResult(
|
|
||||||
templateList,
|
|
||||||
overrides,
|
|
||||||
null,
|
|
||||||
arguments
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (override instanceof List<?> overrides) {
|
if (hasMerges && merge instanceof Map<?, ?> rawMerges) {
|
||||||
// overrides不为空,且不是map
|
Map<ArgumentString, Object> mergeMap = (Map<ArgumentString, Object>) rawMerges;
|
||||||
List<Object> processedOverrides = new ArrayList<>(overrides.size());
|
for (Map.Entry<ArgumentString, Object> inputEntry : mergeMap.entrySet()) {
|
||||||
for (Object item : overrides) {
|
ArgumentString inputKey = inputEntry.getKey();
|
||||||
processUnknownTypeMember(item, arguments, processedOverrides::add);
|
Object key = inputKey.get(parentArguments);
|
||||||
}
|
if (key != null) {
|
||||||
if (input.get(MERGES) instanceof List<?> rawMerges) {
|
merges.put(key.toString(), processUnknownValue(inputEntry.getValue(), arguments));
|
||||||
List<Object> merges = new ArrayList<>(rawMerges.size());
|
}
|
||||||
for (Object item : rawMerges) {
|
|
||||||
processUnknownTypeMember(item, arguments, merges::add);
|
|
||||||
}
|
}
|
||||||
return new TemplateProcessingResult(
|
|
||||||
templateList,
|
|
||||||
processedOverrides,
|
|
||||||
merges,
|
|
||||||
arguments
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return new TemplateProcessingResult(
|
|
||||||
templateList,
|
|
||||||
processedOverrides,
|
|
||||||
null,
|
|
||||||
arguments
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else if (override instanceof String rawOverride) {
|
|
||||||
return new TemplateProcessingResult(
|
|
||||||
templateList,
|
|
||||||
applyArgument(rawOverride, arguments),
|
|
||||||
null,
|
|
||||||
arguments
|
|
||||||
);
|
|
||||||
} else if (override != null) {
|
|
||||||
// overrides不为空,且不是map,list。此情况不用再考虑merge了
|
|
||||||
return new TemplateProcessingResult(
|
return new TemplateProcessingResult(
|
||||||
templateList,
|
templateList,
|
||||||
override,
|
override,
|
||||||
null,
|
merges,
|
||||||
arguments
|
arguments
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// 获取merges
|
return new TemplateProcessingResult(
|
||||||
Object merge = input.get(MERGES);
|
templateList,
|
||||||
if (merge instanceof Map<?, ?> rawMerges) {
|
override,
|
||||||
Map<String, Object> merges = new LinkedHashMap<>();
|
merge,
|
||||||
processMap(MiscUtils.castToMap(rawMerges, false), arguments, (obj) -> {
|
arguments
|
||||||
// 如果merges的下一级就是一个模板,则模板必须为map类型
|
);
|
||||||
if (obj instanceof Map<?,?> mapResult) {
|
|
||||||
merges.putAll(MiscUtils.castToMap(mapResult, false));
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Invalid template used. Input: " + GsonHelper.get().toJson(input) + ". Template: " + GsonHelper.get().toJson(obj));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// 已有template和merges 和可选的arguments
|
|
||||||
if (input.size() > (hasArgument ? 3 : 2)) {
|
|
||||||
// 会不会有一种可能,有笨比用户把模板和普通配置混合在了一起?再次遍历input后处理
|
|
||||||
for (Map.Entry<String, Object> inputEntry : input.entrySet()) {
|
|
||||||
String inputKey = inputEntry.getKey();
|
|
||||||
if (NON_TEMPLATE_KEY.contains(inputKey)) continue;
|
|
||||||
processUnknownTypeMember(inputEntry.getValue(), arguments, (processed) -> merges.put(inputKey, processed));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new TemplateProcessingResult(
|
|
||||||
templateList,
|
|
||||||
null,
|
|
||||||
merges,
|
|
||||||
arguments
|
|
||||||
);
|
|
||||||
} else if (merge instanceof List<?> rawMerges) {
|
|
||||||
List<Object> merges = new ArrayList<>(rawMerges.size());
|
|
||||||
for (Object item : rawMerges) {
|
|
||||||
processUnknownTypeMember(item, arguments, merges::add);
|
|
||||||
}
|
|
||||||
return new TemplateProcessingResult(
|
|
||||||
templateList,
|
|
||||||
null,
|
|
||||||
merges,
|
|
||||||
arguments
|
|
||||||
);
|
|
||||||
} else if (merge instanceof String rawMerge) {
|
|
||||||
// merge是个string
|
|
||||||
return new TemplateProcessingResult(
|
|
||||||
templateList,
|
|
||||||
null,
|
|
||||||
applyArgument(rawMerge, arguments),
|
|
||||||
arguments
|
|
||||||
);
|
|
||||||
} else if (merge != null) {
|
|
||||||
// merge是个普通的类型
|
|
||||||
return new TemplateProcessingResult(
|
|
||||||
templateList,
|
|
||||||
null,
|
|
||||||
merge,
|
|
||||||
arguments
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// 无overrides和merges
|
|
||||||
// 会不会有一种可能,有笨比用户不会使用merges,把模板和普通配置混合在了一起?再次遍历input后处理
|
|
||||||
if (input.size() > (hasArgument ? 2 : 1)) {
|
|
||||||
Map<String, Object> merges = new LinkedHashMap<>();
|
|
||||||
for (Map.Entry<String, Object> inputEntry : input.entrySet()) {
|
|
||||||
String inputKey = inputEntry.getKey();
|
|
||||||
if (NON_TEMPLATE_KEY.contains(inputKey)) continue;
|
|
||||||
processUnknownTypeMember(inputEntry.getValue(), arguments, (processed) -> merges.put(inputKey, processed));
|
|
||||||
}
|
|
||||||
return new TemplateProcessingResult(
|
|
||||||
templateList,
|
|
||||||
null,
|
|
||||||
merges,
|
|
||||||
arguments
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return new TemplateProcessingResult(
|
|
||||||
templateList,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
arguments
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 合并参数
|
// 合并参数
|
||||||
private Map<String, TemplateArgument> mergeArguments(@NotNull Map<String, Object> rawChildArguments,
|
@SuppressWarnings("unchecked")
|
||||||
|
private Map<String, TemplateArgument> mergeArguments(@NotNull Map<ArgumentString, Object> childArguments,
|
||||||
@NotNull Map<String, TemplateArgument> parentArguments) {
|
@NotNull Map<String, TemplateArgument> parentArguments) {
|
||||||
Map<String, TemplateArgument> result = new HashMap<>(parentArguments);
|
Map<String, TemplateArgument> result = new LinkedHashMap<>(parentArguments);
|
||||||
// 我们遍历一下当前节点下的所有参数,这些参数可能含有内嵌参数。所以需要对参数map先处理一次后再合并
|
for (Map.Entry<ArgumentString, Object> argumentEntry : childArguments.entrySet()) {
|
||||||
// arguments:
|
Object placeholderObj = argumentEntry.getKey().get(result);
|
||||||
// argument_1: "{parent_argument}"
|
if (placeholderObj == null) continue;
|
||||||
for (Map.Entry<String, Object> argumentEntry : rawChildArguments.entrySet()) {
|
String placeholder = placeholderObj.toString();
|
||||||
// 获取最终的string形式参数
|
|
||||||
String placeholder = applyArgument(argumentEntry.getKey(), parentArguments).toString();
|
|
||||||
// 父亲参数最大
|
// 父亲参数最大
|
||||||
if (result.containsKey(placeholder)) continue;
|
if (result.containsKey(placeholder)) continue;
|
||||||
Object rawArgument = argumentEntry.getValue();
|
Object processedPlaceholderValue = processUnknownValue(argumentEntry.getValue(), result);
|
||||||
if (rawArgument instanceof Map<?,?> mapArgument) {
|
switch (processedPlaceholderValue) {
|
||||||
// 此参数是一个map,那么对map应用模板,然后再根据map是否含有type等参数,判别其是否为带名特殊参数
|
case Map<?, ?> map -> result.put(placeholder, TemplateArguments.fromMap(MiscUtils.castToMap(map, false)));
|
||||||
Map<String, Object> nestedResult = new LinkedHashMap<>();
|
case List<?> listArgument -> result.put(placeholder, new ListTemplateArgument((List<Object>) listArgument));
|
||||||
processMap(MiscUtils.castToMap(mapArgument, false), parentArguments, (obj) -> {
|
case null -> result.put(placeholder, NullTemplateArgument.INSTANCE);
|
||||||
// 如果有人往arguments下塞了一个模板,则模板类型应为map
|
default -> result.put(placeholder, new ObjectTemplateArgument(processedPlaceholderValue));
|
||||||
if (obj instanceof Map<?,?> mapResult) {
|
|
||||||
nestedResult.putAll(MiscUtils.castToMap(mapResult, false));
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Invalid template used. Input: " + GsonHelper.get().toJson(mapArgument) + ". Template: " + GsonHelper.get().toJson(obj));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
result.put(placeholder, TemplateArguments.fromMap(nestedResult));
|
|
||||||
} else if (rawArgument instanceof List<?> listArgument) {
|
|
||||||
// 此参数是一个list,那么只需要应用模板即可
|
|
||||||
List<Object> nestedResult = new ArrayList<>();
|
|
||||||
for (Object item : listArgument) {
|
|
||||||
processUnknownTypeMember(item, parentArguments, nestedResult::add);
|
|
||||||
}
|
|
||||||
result.put(placeholder, new ListTemplateArgument(nestedResult));
|
|
||||||
} else if (rawArgument == null) {
|
|
||||||
// 使用 null 覆写其父参数内容
|
|
||||||
result.put(placeholder, NullTemplateArgument.INSTANCE);
|
|
||||||
} else if (rawArgument instanceof Number number) {
|
|
||||||
result.put(placeholder, new ObjectTemplateArgument(number));
|
|
||||||
} else if (rawArgument instanceof Boolean booleanValue) {
|
|
||||||
result.put(placeholder, new ObjectTemplateArgument(booleanValue));
|
|
||||||
} else {
|
|
||||||
// 将参数字符串化后,应用参数再放入
|
|
||||||
Object applied = applyArgument(rawArgument.toString(), parentArguments);
|
|
||||||
result.put(placeholder, new ObjectTemplateArgument(applied));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将某个输入变成最终的结果,可以是string->string,也可以是string->map/list
|
|
||||||
private Object applyArgument(String input, Map<String, TemplateArgument> arguments) {
|
|
||||||
// 如果字符串长度连3都没有,那么肯定没有{}啊
|
|
||||||
if (input.length() < 3) return input;
|
|
||||||
if (input.charAt(0) == '{' && input.charAt(input.length() - 1) == '}') {
|
|
||||||
String key = input.substring(1, input.length() - 1);
|
|
||||||
TemplateArgument argument = arguments.get(key);
|
|
||||||
if (argument != null) {
|
|
||||||
return argument.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return replacePlaceholders(input, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
private record TemplateProcessingResult(
|
private record TemplateProcessingResult(
|
||||||
List<Object> templates,
|
List<Object> templates,
|
||||||
Object overrides,
|
Object overrides,
|
||||||
@@ -488,86 +353,4 @@ public class TemplateManagerImpl implements TemplateManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String replacePlaceholders(String input, Map<String, TemplateArgument> replacements) {
|
|
||||||
if (input == null || input.isEmpty()) {
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
StringBuilder finalResult = new StringBuilder();
|
|
||||||
int n = input.length();
|
|
||||||
int lastAppendPosition = 0; // 追踪上一次追加操作结束的位置
|
|
||||||
int i = 0;
|
|
||||||
while (i < n) {
|
|
||||||
// 检查当前字符是否为未转义的 '{'
|
|
||||||
int backslashes = 0;
|
|
||||||
int temp_i = i - 1;
|
|
||||||
while (temp_i >= 0 && input.charAt(temp_i) == '\\') {
|
|
||||||
backslashes++;
|
|
||||||
temp_i--;
|
|
||||||
}
|
|
||||||
if (input.charAt(i) == '{' && backslashes % 2 == 0) {
|
|
||||||
// 发现占位符起点
|
|
||||||
int placeholderStartIndex = i;
|
|
||||||
// 追加从上一个位置到当前占位符之前的文本
|
|
||||||
finalResult.append(input, lastAppendPosition, placeholderStartIndex);
|
|
||||||
// --- 开始解析占位符内部 ---
|
|
||||||
StringBuilder keyBuilder = new StringBuilder();
|
|
||||||
int depth = 1;
|
|
||||||
int j = i + 1;
|
|
||||||
boolean foundMatch = false;
|
|
||||||
while (j < n) {
|
|
||||||
char c = input.charAt(j);
|
|
||||||
if (c == '\\') { // 处理转义
|
|
||||||
if (j + 1 < n) {
|
|
||||||
keyBuilder.append(input.charAt(j + 1));
|
|
||||||
j += 2;
|
|
||||||
} else {
|
|
||||||
keyBuilder.append(c);
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
} else if (c == '{') {
|
|
||||||
depth++;
|
|
||||||
keyBuilder.append(c);
|
|
||||||
j++;
|
|
||||||
} else if (c == '}') {
|
|
||||||
depth--;
|
|
||||||
if (depth == 0) { // 找到匹配的结束括号
|
|
||||||
String key = keyBuilder.toString();
|
|
||||||
TemplateArgument value = replacements.get(key);
|
|
||||||
if (value != null) {
|
|
||||||
// 如果在 Map 中找到值,则进行替换
|
|
||||||
finalResult.append(value.get());
|
|
||||||
} else {
|
|
||||||
// 否则,保留原始占位符(包括 '{}')
|
|
||||||
finalResult.append(input, placeholderStartIndex, j + 1);
|
|
||||||
}
|
|
||||||
// 更新位置指针
|
|
||||||
i = j + 1;
|
|
||||||
lastAppendPosition = i;
|
|
||||||
foundMatch = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
keyBuilder.append(c); // 嵌套的 '}'
|
|
||||||
j++;
|
|
||||||
} else {
|
|
||||||
keyBuilder.append(c);
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// --- 占位符解析结束 ---
|
|
||||||
if (!foundMatch) {
|
|
||||||
// 如果内层循环结束仍未找到匹配的 '}',则不进行任何特殊处理
|
|
||||||
// 外层循环的 i 会自然递增
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 追加最后一个占位符之后的所有剩余文本
|
|
||||||
if (lastAppendPosition < n) {
|
|
||||||
finalResult.append(input, lastAppendPosition, n);
|
|
||||||
}
|
|
||||||
return finalResult.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,22 @@ public class MiscUtils {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> List<T> getAsList(Object o, Class<T> clazz) {
|
||||||
|
if (o instanceof List<?> list) {
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
if (clazz.isInstance(list.getFirst())) {
|
||||||
|
return (List<T>) list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (clazz.isInstance(o)) {
|
||||||
|
return List.of((T) o);
|
||||||
|
}
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
public static Vector3f getAsVector3f(Object o, String option) {
|
public static Vector3f getAsVector3f(Object o, String option) {
|
||||||
if (o == null) return new Vector3f();
|
if (o == null) return new Vector3f();
|
||||||
if (o instanceof List<?> list && list.size() == 3) {
|
if (o instanceof List<?> list && list.size() == 3) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ org.gradle.jvmargs=-Xmx1G
|
|||||||
# Rule: [major update].[feature update].[bug fix]
|
# Rule: [major update].[feature update].[bug fix]
|
||||||
project_version=0.0.56.1
|
project_version=0.0.56.1
|
||||||
config_version=34
|
config_version=34
|
||||||
lang_version=14
|
lang_version=15
|
||||||
project_group=net.momirealms
|
project_group=net.momirealms
|
||||||
latest_supported_version=1.21.5
|
latest_supported_version=1.21.5
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user