mirror of
https://github.com/CarmJos/EasyConfiguration.git
synced 2026-06-04 18:48:20 +08:00
feat(record): Support record type values adapter.
This commit is contained in:
+108
@@ -0,0 +1,108 @@
|
||||
package cc.carm.lib.configured.adapter.record;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||
import cc.carm.lib.configuration.adapter.ValueSerializer;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.RecordComponent;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class RecordAdapter<T extends Record> extends ValueAdapter<T> {
|
||||
|
||||
public static void register(ConfigurationHolder<?> holder) {
|
||||
holder.adapters().register(of(Record.class));
|
||||
}
|
||||
|
||||
public static <R extends Record> RecordAdapter<R> of(@NotNull Class<R> type) {
|
||||
return of(ValueType.of(type));
|
||||
}
|
||||
|
||||
public static <R extends Record> RecordAdapter<R> of(@NotNull ValueType<R> type) {
|
||||
return new RecordAdapter<>(type);
|
||||
}
|
||||
|
||||
public RecordAdapter(@NotNull ValueType<T> type) {
|
||||
super(type, serializer(type), parser(type));
|
||||
}
|
||||
|
||||
public static <R extends Record> ValueSerializer<R> serializer(@NotNull ValueType<R> type) {
|
||||
return (holder, type1, r) -> toMap(holder, r);
|
||||
}
|
||||
|
||||
public static <R extends Record> ValueParser<R> parser(@NotNull ValueType<R> type) {
|
||||
return (holder, valueType, value) -> {
|
||||
if (!(value instanceof ConfigureSection section)) return null;
|
||||
return fromMap(holder, type.getRawType(), section.asMap());
|
||||
};
|
||||
}
|
||||
|
||||
public static <R extends Record> Map<String, Object> toMap(
|
||||
@NotNull ConfigurationHolder<?> holder, @NotNull R record
|
||||
) throws Exception {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
Class<?> recordClass = record.getClass();
|
||||
if (!recordClass.isRecord()) {
|
||||
throw new IllegalArgumentException("Object is not a record");
|
||||
}
|
||||
try {
|
||||
for (RecordComponent component : recordClass.getRecordComponents()) {
|
||||
String name = component.getName();
|
||||
Method accessor = component.getAccessor();
|
||||
Object value = accessor.invoke(record);
|
||||
map.put(name, serializeValue(holder, value));
|
||||
}
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException("Failed to convert record to map", e);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static <R extends Record> R fromMap(
|
||||
@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull Class<R> type, @NotNull Map<String, Object> data
|
||||
) throws Exception {
|
||||
RecordComponent[] components = type.getRecordComponents();
|
||||
Object[] args = new Object[components.length];
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
RecordComponent component = components[i];
|
||||
args[i] = parseValue(holder, component.getType(), data.get(component.getName()));
|
||||
}
|
||||
return createInstance(type, args);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Object parseValue(ConfigurationHolder<?> holder, Class<?> targetType, Object value) throws Exception {
|
||||
if (value == null) return null;
|
||||
if (targetType.isRecord()) {
|
||||
return fromMap(holder, targetType.asSubclass(Record.class), (Map<String, Object>) value);
|
||||
}
|
||||
return holder.deserialize(targetType, value);
|
||||
}
|
||||
|
||||
private static Object serializeValue(ConfigurationHolder<?> holder, Object value) throws Exception {
|
||||
if (value == null) return null;
|
||||
if (value.getClass().isRecord()) {
|
||||
return toMap(holder, (Record) value);
|
||||
}
|
||||
return holder.serialize(value);
|
||||
}
|
||||
|
||||
private static <T> T createInstance(Class<T> t, Object[] args) throws Exception {
|
||||
Class<?>[] paramTypes = Arrays.stream(t.getRecordComponents())
|
||||
.map(RecordComponent::getType).toArray(Class[]::new);
|
||||
Constructor<T> constructor = t.getDeclaredConstructor(paramTypes);
|
||||
constructor.setAccessible(true); // Make sure the constructor is accessible
|
||||
return constructor.newInstance(args);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import cc.carm.lib.configuration.Configuration;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.temp.TempConfigFactory;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||
import cc.carm.lib.configured.adapter.record.RecordAdapter;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class RecordTest {
|
||||
|
||||
interface Config extends Configuration {
|
||||
|
||||
ConfiguredValue<Device> VAL = ConfiguredValue.of(new Device(
|
||||
"device1",
|
||||
"My Device",
|
||||
UUID.fromString("123e4567-e89b-12d3-a456-426614174000"),
|
||||
new Chip("chip1", "SN123456")
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
ConfigurationHolder<?> holder = TempConfigFactory.create().build();
|
||||
RecordAdapter.register(holder);
|
||||
holder.initialize(Config.class);
|
||||
|
||||
System.out.println("Device ID: " + Config.VAL.resolve().id());
|
||||
System.out.println("Device Name: " + Config.VAL.resolve().name());
|
||||
System.out.println("Device Serial: " + Config.VAL.resolve().serial());
|
||||
System.out.println("Chip ID: " + Config.VAL.resolve().chip().id());
|
||||
System.out.println("Chip Serial: " + Config.VAL.resolve().chip().serialNumber());
|
||||
|
||||
try {
|
||||
holder.save();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
printMap(holder.config().asMap(), 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public record Device(String id, String name, UUID serial, Chip chip) {
|
||||
}
|
||||
|
||||
public record Chip(String id, String serialNumber) {
|
||||
}
|
||||
|
||||
static void printMap(Map<String, Object> map, int indent) {
|
||||
String indentStr = " ".repeat(indent);
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
if (entry.getValue() instanceof Map<?, ?> subMap) {
|
||||
System.out.println(indentStr + entry.getKey() + ":");
|
||||
printMap((Map<String, Object>) subMap, indent + 2);
|
||||
} else if (entry.getValue() instanceof List<?> subList) {
|
||||
System.out.println(indentStr + entry.getKey() + ":");
|
||||
for (Object item : subList) {
|
||||
if (item instanceof Map<?, ?> itemMap) {
|
||||
printMap((Map<String, Object>) itemMap, indent + 2);
|
||||
} else {
|
||||
System.out.println(indentStr + " - " + item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
System.out.println(indentStr + entry.getKey() + ": " + entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user