Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Attention][Feature] Rewrite config #21

Open
wants to merge 25 commits into
base: dev/1.21
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1656796
🐺🚧📕🔧💻✔️💾🚀️
AmarokIce Dec 22, 2024
c3f8fa2
🐺🚧📕🔧💻✔️💾🚀️
AmarokIce Dec 22, 2024
053b2bc
🐺📰
AmarokIce Dec 24, 2024
46203a1
🐺🚧
AmarokIce Dec 24, 2024
01c69c9
chore: Apply Spotless
github-actions[bot] Dec 24, 2024
d094dfa
🐺❓
AmarokIce Dec 24, 2024
1cc3730
Merge branch 'dev/config' of https://github.com/KessokuTeaTime/Kessok…
AmarokIce Dec 24, 2024
e487dd3
🐺🚧🚀
AmarokIce Dec 25, 2024
21841a1
chore: Apply Spotless
github-actions[bot] Dec 25, 2024
2aa4eba
🐺🦀
AmarokIce Dec 27, 2024
1e6de53
Merge remote-tracking branch 'origin/dev/config' into dev/config
AmarokIce Dec 27, 2024
55ead24
🐺💾🪡🔩
AmarokIce Dec 27, 2024
a9d2ba1
chore: Apply Spotless
github-actions[bot] Dec 27, 2024
719e663
🐺💾🪡🔩
AmarokIce Jan 13, 2025
14cd07f
chore: Apply Spotless
github-actions[bot] Jan 13, 2025
397f236
🐺🪡🔩🚀
AmarokIce Jan 31, 2025
bd8eabf
Merge branch 'dev/config' of https://github.com/KessokuTeaTime/Kessok…
AmarokIce Jan 31, 2025
782d262
chore: Apply Spotless
github-actions[bot] Jan 31, 2025
beb2460
🐺🚀🎶
AmarokIce Feb 1, 2025
321868d
Merge remote-tracking branch 'origin/dev/config' into dev/config
AmarokIce Feb 1, 2025
63c46aa
chore: Apply Spotless
github-actions[bot] Feb 1, 2025
786ad36
🐺❌
AmarokIce Feb 3, 2025
554f678
chore: Apply Spotless
github-actions[bot] Feb 3, 2025
12bbb73
🐺📰🎶
AmarokIce Feb 4, 2025
194b30a
chore: Apply Spotless
github-actions[bot] Feb 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 15 additions & 20 deletions base/common/src/main/java/band/kessoku/lib/api/KessokuLib.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,9 @@
package band.kessoku.lib.api;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ServiceLoader;
import java.lang.reflect.Modifier;
import java.util.*;

import org.apache.logging.log4j.core.util.ReflectionUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.UnmodifiableView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -33,23 +29,22 @@ public final class KessokuLib {
private KessokuLib() {
}

public static void loadModule(@NotNull final Class<?> moduleCommonClass) {
if (moduleCommonClass.isArray())
throw new UnsupportedOperationException("What the hell are you doing?? KessokuLib.loadModule receives an array class! " + moduleCommonClass.getName());
if (isModuleLoaded(moduleCommonClass)) {
throw new UnsupportedOperationException("Module `" + moduleCommonClass.getName() + "` has already been loaded!");
}
public static void loadModule(Class<?> moduleCommonClass) {
// Try to get module name
final String moduleName;
String moduleName;
try {
final Field field = moduleCommonClass.getField("NAME");
moduleName = (String) ReflectionUtil.getStaticFieldValue(field);
Field field = moduleCommonClass.getField("NAME");
if (!Modifier.isStatic(field.getModifiers())) {
throw new IllegalArgumentException("NAME in " + moduleCommonClass.getPackageName() + " is not static!");
}
field.setAccessible(true);
moduleName = (String) field.get(null);
} catch (NoSuchFieldException e) {
getLogger().error("Invalid Kessoku module! Field `NAME` is not found in {}!", moduleCommonClass.getName());
return;
} catch (NullPointerException e) {
getLogger().error("Invalid Kessoku module! NAME in {} is not static!", moduleCommonClass.getName());
return;
getLogger().warn("no NAME found for {}! Using package name", moduleCommonClass.getPackageName());
moduleName = moduleCommonClass.getPackageName();
} catch (IllegalAccessException e) {
// Already set accessible, shouldn't be called
moduleName = moduleCommonClass.getPackageName();
}
initializedModules.add(moduleCommonClass);
getLogger().info("{} loaded!", moduleName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
*/
package band.kessoku.lib.api.base.reflect;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.util.Arrays;

import band.kessoku.lib.api.KessokuLib;

public final class ReflectUtil {
private ReflectUtil() {
}
Expand All @@ -31,4 +34,14 @@ public static boolean isAssignableFrom(Object o, Class<?>... classes) {
var flag = Arrays.stream(classes).anyMatch(clazz -> !o.getClass().isAssignableFrom(clazz));
return !flag;
}

public static boolean markAccessible(AccessibleObject obj) {
try {
obj.setAccessible(true);
return true;
} catch (Exception e) {
KessokuLib.getLogger().error(e.getMessage(), e);
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
* Copyright (c) 2024 KessokuTeaTime
*
* Licensed under the GNU Lesser General Pubic License, Version 3 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.gnu.org/licenses/lgpl-3.0.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package band.kessoku.lib.api.config;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Path;
import java.util.*;

import band.kessoku.lib.api.KessokuLib;
import band.kessoku.lib.api.base.reflect.ModifiersUtil;
import band.kessoku.lib.api.base.reflect.ReflectUtil;
import band.kessoku.lib.api.config.api.*;
import club.someoneice.json.Pair;
import com.google.common.collect.*;
import com.google.common.io.Files;

/**
* The config handler, also see {@link Config Config}. <br>
* The config format or file type determined by config codec.
*
* @see Config Config
* @see Codec Codec
* @see ConfigBasicCodec Basic Config Codec
*
* @author AmarokIce
*/
public final class ConfigHandler {
private static Path configDir;
/**
* All mods config(s).
*/
private static final Table<String, String, Pair<Class<?>, ConfigHandler>> MOD_CONFIG_DATA = HashBasedTable.create();

/**
* Record all config fields, next time to use needn't scan it again.
*
* @see ConfigHandler#readByClass(Class, ConfigHandler)
* @see ConfigHandler#saveToClass(Class, ConfigHandler, Map)
*/
private ImmutableList<Field> fields;

/**
* Format codec for {@code Config} and {@code Category}. <br>
*
* @see Category
*/
private final Codec<Map<String, ConfigData>> formatCodec;
private final Class<?> clazz;

private ConfigHandler(Codec<Map<String, ConfigData>> codec, Class<?> clazz) {
this.formatCodec = codec;
this.clazz = clazz;
}

private static void registerConfig(Class<?> clazz) throws IllegalAccessException, IOException {
Config configSetting = clazz.getAnnotation(Config.class);
final String modid = configSetting.value();
final String name = configSetting.name().isEmpty() ? configSetting.value() : configSetting.name();
final var codec = ConfigBasicCodec.getCodec(configSetting.codec());

ConfigHandler configHandler = new ConfigHandler(codec, clazz);
MOD_CONFIG_DATA.put(modid, name, new Pair<>(clazz, configHandler));

var defMap = readByClass(clazz, configHandler);

File cfgFile = ConfigHandler.configDir.resolve(name).toFile();
if (!cfgFile.exists()) {
cfgFile.createNewFile();
}

String data = Arrays.toString(Files.toByteArray(ConfigHandler.configDir.toFile()));
var localMap = codec.encode(data);

Map<String, ConfigData> commands = Maps.newLinkedHashMap();
for (Map.Entry<String, ConfigData> entry : defMap.entrySet()) {
String key = entry.getKey();
commands.put(key, localMap.containsKey(key) ? localMap.get(key) : entry.getValue());
}

saveToClass(clazz, configHandler, commands);
data = codec.decode(commands);
Files.write(data.getBytes(), cfgFile);
}

public static Map<String, ConfigData> readByClass(Class<?> clazz, ConfigHandler cfg) throws IllegalAccessException {
Map<String, ConfigData> map = Maps.newLinkedHashMap();

if (Objects.isNull(cfg.fields)) {
scanFields(clazz, cfg);
}

for (Field field : cfg.fields) {
String name = field.isAnnotationPresent(Name.class) ? field.getAnnotation(Name.class).value() : field.getName();
List<String> comments = field.isAnnotationPresent(Comment.class) ? Lists.newArrayList(field.getAnnotation(Comment.class).value()) : Collections.emptyList();

String data = ReflectUtil.isAssignableFrom(field, Category.class)
? cfg.formatCodec.decode(readByClass(field.getType(), cfg))
: ((ConfigValue<?>) field.get(null)).decode();

map.put(name, new ConfigData(name, data, comments));
}

return map;
}

public static void saveToClass(Class<?> clazz, ConfigHandler cfg, Map<String, ConfigData> data) throws IllegalAccessException {
if (Objects.isNull(cfg.fields)) {
scanFields(clazz, cfg);
}

for (Field field : cfg.fields) {
String name = field.isAnnotationPresent(Name.class) ? field.getAnnotation(Name.class).value() : field.getName();
var raw = ((ConfigValue<?>) field.get(null));
if (ReflectUtil.isAssignableFrom(field, Category.class)) {
saveToClass(field.getType(), cfg, cfg.formatCodec.encode(data.get(name).rawValue()));
} else {
raw.encode(data.get(name).rawValue());
}
}
}

public static void scanFields(Class<?> clazz, ConfigHandler cfg) {
ImmutableList.Builder<Field> builder = ImmutableList.builder();
for (Field field : clazz.getDeclaredFields()) {
if (!ModifiersUtil.isStatic(field)) {
continue;
}

if (!ReflectUtil.isAssignableFrom(field, ConfigValue.class)) {
continue;
}

ReflectUtil.markAccessible(field);
builder.add(field);
}

cfg.fields = builder.build();
}

/**
* The magic method for auto scan all config class with {@link Config @Config}.
*
* @see Config @Config
* @param configDir The based config directory, get by loader.
*/
public static void handleConfigs(List<Class<?>> list, Path configDir) {
ConfigHandler.configDir = configDir;
list.forEach(it -> {
try {
registerConfig(it);
} catch (Exception e) {
KessokuLib.getLogger().error(e.getMessage(), e);
}
});
}
}

This file was deleted.

Loading