From b049b08b3ee6402e6b1576d68566b7eff95ec85b Mon Sep 17 00:00:00 2001 From: Sky Date: Sat, 18 Jan 2025 22:14:43 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20Add=20validation=20for=20require?= =?UTF-8?q?d=20fields=20in=20data=20structures?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a `validateFields` method to ensure that all required fields in a data structure are non-null. Unexpected nodes now raise errors for better debugging and validation clarity. Improved planning and handling of keys during parsing. --- .../api/datastruct/DataStructureFactory.java | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/itsthesky/disky/api/datastruct/DataStructureFactory.java b/src/main/java/net/itsthesky/disky/api/datastruct/DataStructureFactory.java index ca1686a3..9eaa15ef 100644 --- a/src/main/java/net/itsthesky/disky/api/datastruct/DataStructureFactory.java +++ b/src/main/java/net/itsthesky/disky/api/datastruct/DataStructureFactory.java @@ -1,5 +1,6 @@ package net.itsthesky.disky.api.datastruct; +import ch.njol.skript.Skript; import ch.njol.skript.config.Node; import ch.njol.skript.config.SectionNode; import ch.njol.skript.lang.Expression; @@ -43,6 +44,7 @@ public final class DataStructureFactory { * containing all necessary information to create instances. */ public static record DataStructureParseResult( + @NotNull SectionNode relatedNode, @NotNull EntryValidator validator, @NotNull EntryContainer container, @NotNull Map>> expressions, @@ -78,7 +80,7 @@ public static DataStructureParseResult initDataStructure( return null; } - return new DataStructureParseResult(validator, container, expressions, subStructures); + return new DataStructureParseResult(node, validator, container, expressions, subStructures); } /** @@ -91,15 +93,22 @@ public static EntryValidator createValidator(@NotNull Class structClass) { final var validator = EntryValidator.builder(); DiSky.debug("=========== Starting validation of data structure " + structClass.getName() + " ==========="); + final var plannedKeys = new HashSet(); for (final var field : structClass.getDeclaredFields()) { final var entry = field.getAnnotation(DataStructureEntry.class); - if (entry == null) continue; + if (entry == null) + continue; processFieldValidator(validator, field, entry, structClass); + plannedKeys.add(entry.value()); } validator.unexpectedNodeTester(node -> { - DiSky.debug("UNEXPECTED NODE: " + node.getKey()); + final var key = node.getKey().split(":")[0]; + if (plannedKeys.contains(key)) + return false; // all good, it's a sub-structure or entry + + Skript.error("Unexpected node with key '" + key + "' in data structure " + structClass.getSimpleName()); return false; }); @@ -129,11 +138,38 @@ public static Object createDataStructure( final var unhandledNodes = groupUnhandledNodes(container); processFields(structClass, instance, parseResult, event, unhandledNodes); + if (!validateFields(parseResult.relatedNode(), structClass, instance)) + return null; DiSky.debug("################## End of creation of data structure " + structClass.getName() + " ##################"); return finalizeInstance(instance, chainInstance); } + /** + * Validates the fields of a data structure instance. + * This will check the instance's field value for optional and type constraints. + * @param structClass The class of the data structure + * @param instance The instance to validate + */ + private static boolean validateFields(@NotNull Node node, @NotNull Class structClass, @NotNull Object instance) { + for (final var field : structClass.getDeclaredFields()) { + final var entry = field.getAnnotation(DataStructureEntry.class); + if (entry == null) continue; + + final var key = entry.value(); + final var value = ReflectionUtils.getFieldValue(field, instance); + + if (value == null && !entry.optional()) { + DiSkyRuntimeHandler.error(new IllegalStateException( + "Field '" + key + "' in data structure " + structClass.getSimpleName() + " is null/none/empty, but is required!" + ), node, false); + return false; + } + } + + return true; + } + /** * Maps field keys to their corresponding field names. */