9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2026-01-04 15:41:38 +00:00

改进模板系统

This commit is contained in:
XiaoMoMi
2025-06-07 03:33:43 +08:00
parent 8d7507a49c
commit 9ff52ed15e
16 changed files with 592 additions and 458 deletions

View File

@@ -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>"

View File

@@ -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>"

View File

@@ -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());

View File

@@ -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))
);
}
}
}

View File

@@ -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;
} }

View File

@@ -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;
} }

View File

@@ -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;
} }

View File

@@ -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;
} }
} }

View File

@@ -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;
} }

View File

@@ -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;

View File

@@ -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);
} }

View File

@@ -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) {

View File

@@ -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);
};
}
} }

View File

@@ -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);
// 已有templatemergesoverrides 和可选的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 {
// 已有templateoverrides 和可选的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();
}
} }

View File

@@ -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) {

View File

@@ -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