Skip to content
This repository was archived by the owner on Jun 11, 2026. It is now read-only.

Commit 3183eb2

Browse files
committed
improve array valuetype handling
1 parent 95c5b92 commit 3183eb2

4 files changed

Lines changed: 97 additions & 47 deletions

File tree

src/main/java/org/comroid/api/config/ConfigurationManager.java

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@
3232
import org.comroid.api.data.bind.DataStructure;
3333
import org.comroid.api.data.seri.DataNode;
3434
import org.comroid.api.data.seri.MimeType;
35+
import org.comroid.api.data.seri.type.ArrayValueType;
3536
import org.comroid.api.func.exc.ThrowingFunction;
3637
import org.comroid.api.func.exc.ThrowingSupplier;
3738
import org.comroid.api.func.ext.Context;
3839
import org.comroid.api.func.ext.Wrap;
3940
import org.comroid.api.func.util.Debug;
4041
import org.comroid.api.func.util.Pair;
4142
import org.comroid.api.io.FileHandle;
43+
import org.comroid.api.java.Activator;
4244
import org.comroid.api.java.JITAssistant;
4345
import org.comroid.api.text.Capitalization;
4446
import org.comroid.api.tree.UncheckedCloseable;
@@ -49,6 +51,7 @@
4951
import java.io.FileInputStream;
5052
import java.io.FileOutputStream;
5153
import java.io.InputStreamReader;
54+
import java.lang.reflect.Array;
5255
import java.lang.reflect.Field;
5356
import java.nio.charset.StandardCharsets;
5457
import java.time.Instant;
@@ -89,7 +92,8 @@ public ConfigurationManager(Context context, Class<T> type, String filePath, Mim
8992
}
9093

9194
public T initialize() {
92-
if (!file.exists() && (file.getParentFile().exists() || file.getParentFile().mkdirs())) save(); // save default config
95+
if (!file.exists() && (file.getParentFile().exists() || file.getParentFile()
96+
.mkdirs())) save(); // save default config
9397
reload();
9498
return config;
9599
}
@@ -103,9 +107,13 @@ public void reload(boolean force) {
103107
if (!force && ftime().isBefore(timestamp)) return; // reload is not necessary
104108

105109
DataNode node;
106-
try (var fis = new FileInputStream(file); var isr = new InputStreamReader(fis); var br = new BufferedReader(isr)) {
110+
try (
111+
var fis = new FileInputStream(file); var isr = new InputStreamReader(fis);
112+
var br = new BufferedReader(isr)
113+
) {
107114
var data = br.lines().collect(Collectors.joining());
108-
node = Objects.requireNonNull(dataType.getDeserializer(), "No deserializer set for " + dataType).apply(data);
115+
node = Objects.requireNonNull(dataType.getDeserializer(), "No deserializer set for " + dataType)
116+
.apply(data);
109117
}
110118
Objects.requireNonNull(node, "No data");
111119

@@ -123,18 +131,30 @@ private void setSelfAndChildrenRecursive(DataStructure<?> struct, Object it, Dat
123131
classes = property.getAnnotation(Adapt.class).value();
124132
JITAssistant.prepare(classes);
125133
value = Arrays.stream(classes)
126-
.flatMap($ -> Stream.ofNullable(TypeAdapter.CACHE.getOrDefault(property.getType().getTargetClass(), null)))
134+
.flatMap($ -> Stream.ofNullable(TypeAdapter.CACHE.getOrDefault(property.getType()
135+
.getTargetClass(), null)))
127136
.flatMap(adp -> adp.deserialize(context, adp.parseSerialized(node.asString())).stream())
128137
.findAny()
129138
.orElse(null);
130139
}
131-
if (propType.isStandard() || classes != null) {
140+
141+
if (propType instanceof ArrayValueType<?>) {
142+
var componentType = propType.getTargetClass().getComponentType();
143+
var array = (Object[]) Array.newInstance(componentType, node.size());
144+
for (int i = 0; i < node.size(); i++) {
145+
var each = node.get(i);
146+
array[i] = Activator.get(componentType).createInstance(each);
147+
}
148+
value = array;
149+
}
150+
151+
if (propType.isStandard() || propType.isArray() || classes != null) {
132152
if (propType.isStandard() && value == null) value = propType.parse(node.asString());
133153
property.setFor(it, uncheckedCast(value));
134154
} else //noinspection ConstantValue <- false positive for some reason
135155
if (node != null) setSelfAndChildrenRecursive(DataStructure.of(propType.getTargetClass()),
136-
property.getFrom(it),
137-
node);
156+
property.getFrom(it),
157+
node);
138158
}
139159
}
140160

@@ -228,17 +248,23 @@ public void clear() {
228248
@Override
229249
public void refresh() {
230250
for (var property : struct.getProperties())
231-
if (!property.isAnnotationPresent(Ignore.class)) sendAttributeMessageRecursive(property.getName(), property, config, 1);
251+
if (!property.isAnnotationPresent(Ignore.class)) sendAttributeMessageRecursive(property.getName(),
252+
property,
253+
config,
254+
1);
232255
}
233256

234-
private void sendAttributeMessageRecursive(String fullName, DataStructure<?>.Property<?> property, Object it, int level) {
257+
private void sendAttributeMessageRecursive(
258+
String fullName, DataStructure<?>.Property<?> property, Object it, int level) {
235259
if (it == null) {
236260
Debug.log(fullName + " is null");
237261
return;
238262
}
239263
Debug.log(fullName + " is not null");
240264

241-
var title = IntStream.range(0, level).mapToObj($ -> "#").collect(Collectors.joining()) + " Config Value " + Code.apply(fullName);
265+
var title = IntStream.range(0, level)
266+
.mapToObj($ -> "#")
267+
.collect(Collectors.joining()) + " Config Value " + Code.apply(fullName);
242268
var desc = property.getDescription();
243269
var current = property.getFrom(it);
244270
var propType = property.getType();
@@ -248,7 +274,10 @@ private void sendAttributeMessageRecursive(String fullName, DataStructure<?>.Pro
248274
```
249275
%s: %s
250276
```
251-
""".formatted(title, desc.isEmpty() ? "" : "\n> " + String.join("\n> ", desc), propType.getTargetClass().getSimpleName(), current);
277+
""".formatted(title,
278+
desc.isEmpty() ? "" : "\n> " + String.join("\n> ", desc),
279+
propType.getTargetClass().getSimpleName(),
280+
current);
252281

253282
ifs:
254283
{
@@ -267,15 +296,20 @@ private void sendAttributeMessageRecursive(String fullName, DataStructure<?>.Pro
267296
if (current instanceof ISnowflake flake) current = flake.getIdLong();
268297
if (current != null) {
269298
var currentId = (long) current;
270-
if (Channel.class.isAssignableFrom(propClass)) def = EntitySelectMenu.DefaultValue.channel(currentId);
271-
else if (User.class.isAssignableFrom(propClass)) def = EntitySelectMenu.DefaultValue.user(currentId);
272-
else if (Role.class.isAssignableFrom(propClass)) def = EntitySelectMenu.DefaultValue.role(currentId);
273-
else throw new IllegalArgumentException("Invalid mentionable: " + propClass.getCanonicalName());
299+
if (Channel.class.isAssignableFrom(propClass)) def = EntitySelectMenu.DefaultValue.channel(
300+
currentId);
301+
else if (User.class.isAssignableFrom(propClass)) def = EntitySelectMenu.DefaultValue.user(
302+
currentId);
303+
else if (Role.class.isAssignableFrom(propClass)) def = EntitySelectMenu.DefaultValue.role(
304+
currentId);
305+
else
306+
throw new IllegalArgumentException("Invalid mentionable: " + propClass.getCanonicalName());
274307
builder.setDefaultValues(def);
275308
}
276309

277310
// choose ChannelType if necessary
278-
if (target == EntitySelectMenu.SelectTarget.CHANNEL) builder.setChannelTypes(Arrays.stream(ChannelType.values())
311+
if (target == EntitySelectMenu.SelectTarget.CHANNEL) builder.setChannelTypes(Arrays.stream(
312+
ChannelType.values())
279313
.filter(type -> type.getInterface().isAssignableFrom(propClass))
280314
.filter(type -> type != ChannelType.UNKNOWN)
281315
.toList());
@@ -290,7 +324,8 @@ private void sendAttributeMessageRecursive(String fullName, DataStructure<?>.Pro
290324
.filter(Field::isEnumConstant)
291325
.forEach(field -> menu.addOption(Aliased.$(field)
292326
.findAny()
293-
.or(() -> Optional.ofNullable(ThrowingSupplier.sneaky(() -> Named.$(field.get(null))).get()))
327+
.or(() -> Optional.ofNullable(ThrowingSupplier.sneaky(() -> Named.$(field.get(null)))
328+
.get()))
294329
.orElseGet(field::getName), field.getName(), Annotations.descriptionText(field)));
295330

296331
// send enum selection box
@@ -358,7 +393,8 @@ public void onStringSelectInteraction(@NotNull StringSelectInteractionEvent even
358393
.filter(Field::isEnumConstant)
359394
.filter(field -> value.equals(Aliased.$(field)
360395
.findAny()
361-
.or(() -> Optional.ofNullable(ThrowingSupplier.sneaky(() -> Named.$(field.get(null))).get()))
396+
.or(() -> Optional.ofNullable(ThrowingSupplier.sneaky(() -> Named.$(field.get(
397+
null))).get()))
362398
.orElseGet(field::getName))))
363399
.findAny()
364400
.map(ThrowingFunction.sneaky(field -> field.get(null)))
@@ -389,33 +425,44 @@ public void onEntitySelectInteraction(@NotNull EntitySelectInteractionEvent even
389425
private CompletableFuture<?> updateDisplayValue(Message original, InteractionHook hook, Object value) {
390426
var raw = original.getContentRaw();
391427
var start = raw.indexOf("```");
392-
return original.editMessage(raw.substring(0, start) + "```\n" + value + "\n```").flatMap($ -> hook.deleteOriginal()).submit();
428+
return original.editMessage(raw.substring(0, start) + "```\n" + value + "\n```")
429+
.flatMap($ -> hook.deleteOriginal())
430+
.submit();
393431
}
394432

395433
private Pair<DataStructure<?>.Property<?>, Object> descend(String... path) {
396434
if (path.length == 0) throw new IllegalArgumentException("Empty path");
397435
Wrap<DataStructure<?>.Property<?>> wrap = uncheckedCast(struct.getProperty(path[0]));
398436
Object holder = config;
399-
if (wrap.test(prop -> !prop.getType().isStandard())) holder = wrap.ifPresentMap(prop -> prop.getFrom(config));
437+
if (wrap.test(prop -> !prop.getType()
438+
.isStandard())) holder = wrap.ifPresentMap(prop -> prop.getFrom(config));
400439
for (var i = 1; i < path.length; i++) {
401440
final var fi = i;
402441
final var fh = holder;
403-
wrap = wrap.map(it -> it.getType().getTargetClass()).map(DataStructure::of).flatMap(struct -> struct.getProperty(path[fi]));
442+
wrap = wrap.map(it -> it.getType().getTargetClass())
443+
.map(DataStructure::of)
444+
.flatMap(struct -> struct.getProperty(path[fi]));
404445
if (wrap.test(prop -> !prop.getType().isStandard() && prop.getType().getTargetClass().isInstance(fh))) {
405446
Object o = wrap.ifPresentMap(prop -> prop.getFrom(fh));
406447
if (o != null) holder = o;
407448
}
408449
}
409450
final var fh = holder;
410-
return wrap.ifPresentMapOrElseThrow(prop -> new Pair<>(prop, fh), () -> new NoSuchElementException("No such property: " + String.join(".", path)));
451+
return wrap.ifPresentMapOrElseThrow(prop -> new Pair<>(prop, fh),
452+
() -> new NoSuchElementException("No such property: " + String.join(".", path)));
411453
}
412454

413455
private static EntitySelectMenu.@NotNull SelectTarget getSelectTarget(DataStructure<?>.Property<?> property) {
414456
EntitySelectMenu.SelectTarget target;
415-
if (Channel.class.isAssignableFrom(property.getType().getTargetClass())) target = EntitySelectMenu.SelectTarget.CHANNEL;
416-
else if (User.class.isAssignableFrom(property.getType().getTargetClass())) target = EntitySelectMenu.SelectTarget.USER;
417-
else if (Role.class.isAssignableFrom(property.getType().getTargetClass())) target = EntitySelectMenu.SelectTarget.ROLE;
418-
else throw new IllegalArgumentException("Invalid mentionable: " + property.getType().getTargetClass().getCanonicalName());
457+
if (Channel.class.isAssignableFrom(property.getType()
458+
.getTargetClass())) target = EntitySelectMenu.SelectTarget.CHANNEL;
459+
else if (User.class.isAssignableFrom(property.getType()
460+
.getTargetClass())) target = EntitySelectMenu.SelectTarget.USER;
461+
else if (Role.class.isAssignableFrom(property.getType()
462+
.getTargetClass())) target = EntitySelectMenu.SelectTarget.ROLE;
463+
else throw new IllegalArgumentException("Invalid mentionable: " + property.getType()
464+
.getTargetClass()
465+
.getCanonicalName());
419466
return target;
420467
}
421468
}

src/main/java/org/comroid/api/data/seri/type/ArrayValueType.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ public class ArrayValueType<T> implements ValueType<T>, HtmlReadonlyStringInputD
2222
public static final Map<Class<?>, ArrayValueType<?>> cache = Collections.unmodifiableMap($cache);
2323

2424
public static <T> ArrayValueType<T> of(Class<? extends T> type) {
25-
return Polyfill.uncheckedCast($cache.computeIfAbsent(type.arrayType(), ArrayValueType::new));
25+
return Polyfill.uncheckedCast($cache.computeIfAbsent(type.isArray() ? type : type.arrayType(),
26+
ArrayValueType::new));
2627
}
2728

2829
Class<T> targetClass;

src/main/java/org/comroid/api/data/seri/type/StandardValueType.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -88,20 +88,20 @@ public class StandardValueType<R> implements ValueType<R>, HtmlInputDesc {
8888
"text",
8989
"pattern='" + RegExpUtil.UUID4_PATTERN + "'");
9090
public static final StandardValueType<Void> VOID = new StandardValueType<>(void.class, "Void", () -> null, it -> null, "hidden");
91-
/**
92-
* @deprecated use {@link BoundValueType}
93-
*/
94-
@Deprecated(forRemoval = true)
95-
public static final StandardValueType<Object> OBJECT = new StandardValueType<>(Object.class, "Object", () -> null, it -> it, "hidden");
96-
/**
97-
* @deprecated use {@link ArrayValueType}
98-
*/
99-
@Deprecated(forRemoval = true)
100-
public static final StandardValueType<Object[]> ARRAY = new StandardValueType<>(Object[].class,
101-
"Array",
102-
() -> new Object[0],
103-
it -> new Object[]{ it },
104-
"hidden");
91+
// /**
92+
// * @deprecated use {@link BoundValueType}
93+
// */
94+
// @Deprecated(forRemoval = true)
95+
// public static final StandardValueType<Object> OBJECT = new StandardValueType<>(Object.class, "Object", () -> null, it -> it, "hidden");
96+
// /**
97+
// * @deprecated use {@link ArrayValueType}
98+
// */
99+
// @Deprecated(forRemoval = true)
100+
// public static final StandardValueType<Object[]> ARRAY = new StandardValueType<>(Object[].class,
101+
// "Array",
102+
// () -> new Object[0],
103+
// it -> new Object[]{ it },
104+
// "hidden");
105105

106106
@Experimental
107107
public static Object findGoodType(String parse) {

src/main/java/org/comroid/api/data/seri/type/ValueType.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616
import java.util.function.Function;
1717
import java.util.function.Predicate;
1818

19-
public interface ValueType<R> extends ValuePointer<R>, Predicate<Object>, Named, HtmlFormElementDesc, Specifiable<ValueType<R>>, Default.Extension {
19+
public interface ValueType<R>
20+
extends ValuePointer<R>, Predicate<Object>, Named, HtmlFormElementDesc, Specifiable<ValueType<R>>,
21+
Default.Extension {
2022
static <T> ValueType<T> of(final Class<?> type) {
2123
return StandardValueType.forClass(type)
24+
.or(() -> type.isArray() ? ArrayValueType.of(type.getComponentType()) : null)
2225
.or(() -> BoundValueType.of(type))
2326
.cast();
2427
}
@@ -70,7 +73,8 @@ default Function<String, R> getConverter() {
7073
@Nullable
7174
default Object defaultValue() {
7275
return ReflectionHelper.fieldWithAnnotation(getTargetClass(), Default.class)
73-
.stream().findAny()
76+
.stream()
77+
.findAny()
7478
.map(fld -> ReflectionHelper.forceGetField(null, fld))
7579
.orElse(null);
7680
}
@@ -81,10 +85,8 @@ default boolean test(Object it) {
8185
}
8286

8387
default <T> T convert(R value, ValueType<T> toType) {
84-
if (equals(toType))
85-
return Polyfill.uncheckedCast(value);
86-
if (value == null)
87-
return null;
88+
if (equals(toType)) return Polyfill.uncheckedCast(value);
89+
if (value == null) return null;
8890
return toType.parse(value.toString());
8991
}
9092
}

0 commit comments

Comments
 (0)