diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFetcher.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFetcher.java new file mode 100644 index 00000000000..dbfef7cc08f --- /dev/null +++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructFetcher.java @@ -0,0 +1,65 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package edu.wpi.first.util.struct; + +import java.util.Optional; + +/** + * A utility class for fetching the assigned struct of existing classes. These are usually public, + * static, and final properties with the Struct type. + */ +public final class StructFetcher { + private StructFetcher() { + throw new UnsupportedOperationException("This is a utility class!"); + } + + /** + * Returns a {@link Struct} for the given {@link StructSerializable} marked class. Due to the + * non-contractual nature of the marker this can fail. If the {@code struct} field could not be + * accessed for any reason, an empty {@link Optional} is returned. + * + * @param The type of the class. + * @param clazz The class object to extract the struct from. + * @return An optional containing the struct if it could be extracted. + */ + @SuppressWarnings("unchecked") + public static Optional> fetchStruct( + Class clazz) { + try { + var possibleField = Optional.ofNullable(clazz.getDeclaredField("struct")); + return possibleField.flatMap( + field -> { + if (Struct.class.isAssignableFrom(field.getType())) { + try { + return Optional.ofNullable((Struct) field.get(null)); + } catch (IllegalAccessException e) { + return Optional.empty(); + } + } else { + return Optional.empty(); + } + }); + } catch (NoSuchFieldException e) { + return Optional.empty(); + } + } + + /** + * Returns a {@link Struct} for the given class. This does not do compile time checking that the + * class is a {@link StructSerializable}. Whenever possible it is reccomended to use {@link + * #fetchStruct(Class)}. + * + * @param clazz The class object to extract the struct from. + * @return An optional containing the struct if it could be extracted. + */ + @SuppressWarnings("unchecked") + public static Optional> fetchStructDynamic(Class clazz) { + if (StructSerializable.class.isAssignableFrom(clazz)) { + return fetchStruct((Class) clazz).map(struct -> struct); + } else { + return Optional.empty(); + } + } +} diff --git a/wpiutil/src/main/java/edu/wpi/first/util/struct/ProceduralStructGenerator.java b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructGenerator.java similarity index 88% rename from wpiutil/src/main/java/edu/wpi/first/util/struct/ProceduralStructGenerator.java rename to wpiutil/src/main/java/edu/wpi/first/util/struct/StructGenerator.java index 213a16289f0..24b2541e1c2 100644 --- a/wpiutil/src/main/java/edu/wpi/first/util/struct/ProceduralStructGenerator.java +++ b/wpiutil/src/main/java/edu/wpi/first/util/struct/StructGenerator.java @@ -12,11 +12,10 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Optional; /** A utility class for procedurally generating {@link Struct}s from records and enums. */ -public final class ProceduralStructGenerator { - private ProceduralStructGenerator() { +public final class StructGenerator { + private StructGenerator() { throw new UnsupportedOperationException("This is a utility class!"); } @@ -123,54 +122,6 @@ public static void addCustomStruct(Class clazz, Struct struct, boolean } } - /** - * Returns a {@link Struct} for the given {@link StructSerializable} marked class. Due to the - * non-contractual nature of the marker this can fail. If the {@code struct} field could not be - * accessed for any reason, an empty {@link Optional} is returned. - * - * @param The type of the class. - * @param clazz The class object to extract the struct from. - * @return An optional containing the struct if it could be extracted. - */ - @SuppressWarnings("unchecked") - public static Optional> extractClassStruct( - Class clazz) { - try { - var possibleField = Optional.ofNullable(clazz.getDeclaredField("struct")); - return possibleField.flatMap( - field -> { - if (Struct.class.isAssignableFrom(field.getType())) { - try { - return Optional.ofNullable((Struct) field.get(null)); - } catch (IllegalAccessException e) { - return Optional.empty(); - } - } else { - return Optional.empty(); - } - }); - } catch (NoSuchFieldException e) { - return Optional.empty(); - } - } - - /** - * Returns a {@link Struct} for the given class. This does not do compile time checking that the - * class is a {@link StructSerializable}. Whenever possible it is reccomended to use {@link - * #extractClassStruct(Class)}. - * - * @param clazz The class object to extract the struct from. - * @return An optional containing the struct if it could be extracted. - */ - @SuppressWarnings("unchecked") - public static Optional> extractClassStructDynamic(Class clazz) { - if (StructSerializable.class.isAssignableFrom(clazz)) { - return extractClassStruct((Class) clazz).map(struct -> struct); - } else { - return Optional.empty(); - } - } - /** A utility for building schema syntax in a procedural manner. */ @SuppressWarnings("PMD.AvoidStringBufferField") public static class SchemaBuilder { @@ -303,7 +254,7 @@ public boolean isImmutable() { * @return The generated struct. */ @SuppressWarnings({"unchecked", "PMD.AvoidAccessibilityAlteration"}) - static Struct genRecord(final Class recordClass) { + public static Struct genRecord(final Class recordClass) { final RecordComponent[] components = recordClass.getRecordComponents(); final SchemaBuilder schemaBuilder = new SchemaBuilder(); final ArrayList> nestedStructs = new ArrayList<>(); @@ -329,7 +280,7 @@ static Struct genRecord(final Class recordClass) { if (customStructTypeMap.containsKey(type)) { struct = customStructTypeMap.get(type); } else if (StructSerializable.class.isAssignableFrom(type)) { - var optStruct = extractClassStructDynamic(type); + var optStruct = StructFetcher.fetchStructDynamic(type); if (optStruct.isPresent()) { struct = optStruct.get(); } else { @@ -465,7 +416,7 @@ public boolean isImmutable() { * @return The generated struct. */ @SuppressWarnings({"unchecked", "PMD.AvoidAccessibilityAlteration"}) - static > Struct genEnum(Class enumClass) { + public static > Struct genEnum(Class enumClass) { final E[] enumVariants = enumClass.getEnumConstants(); final Field[] allEnumFields = enumClass.getDeclaredFields(); final SchemaBuilder schemaBuilder = new SchemaBuilder(); @@ -516,7 +467,7 @@ static > Struct genEnum(Class enumClass) { if (customStructTypeMap.containsKey(type)) { struct = customStructTypeMap.get(type); } else if (StructSerializable.class.isAssignableFrom(type)) { - var optStruct = extractClassStructDynamic(type); + var optStruct = StructFetcher.fetchStructDynamic(type); if (optStruct.isPresent()) { struct = optStruct.get(); } else { diff --git a/wpiutil/src/test/java/edu/wpi/first/util/struct/ProceduralStructGeneratorTest.java b/wpiutil/src/test/java/edu/wpi/first/util/struct/StructGeneratorTest.java similarity index 90% rename from wpiutil/src/test/java/edu/wpi/first/util/struct/ProceduralStructGeneratorTest.java rename to wpiutil/src/test/java/edu/wpi/first/util/struct/StructGeneratorTest.java index ccdbca30564..c5a1eea25be 100644 --- a/wpiutil/src/test/java/edu/wpi/first/util/struct/ProceduralStructGeneratorTest.java +++ b/wpiutil/src/test/java/edu/wpi/first/util/struct/StructGeneratorTest.java @@ -4,15 +4,15 @@ package edu.wpi.first.util.struct; -import static edu.wpi.first.util.struct.ProceduralStructGenerator.genEnum; -import static edu.wpi.first.util.struct.ProceduralStructGenerator.genRecord; +import static edu.wpi.first.util.struct.StructGenerator.genEnum; +import static edu.wpi.first.util.struct.StructGenerator.genRecord; import static org.junit.jupiter.api.Assertions.assertEquals; import java.nio.ByteBuffer; import java.nio.ByteOrder; import org.junit.jupiter.api.Test; -class ProceduralStructGeneratorTest { +class StructGeneratorTest { public record CustomRecord(int int32, boolean bool, double float64, char character, short int16) implements StructSerializable { public static CustomRecord create() { @@ -95,8 +95,7 @@ public final int hashCode() { @SuppressWarnings("unchecked") private void testStructRoundTrip(S value) { - Struct struct = - ProceduralStructGenerator.extractClassStruct((Class) value.getClass()).get(); + Struct struct = StructFetcher.fetchStruct((Class) value.getClass()).get(); ByteBuffer buffer = ByteBuffer.allocate(struct.getSize()); buffer.order(ByteOrder.LITTLE_ENDIAN); struct.pack(buffer, value); @@ -108,8 +107,7 @@ private void testStructRoundTrip(S value) { @SuppressWarnings("unchecked") private void testStructDoublePack(S value) { - Struct struct = - ProceduralStructGenerator.extractClassStruct((Class) value.getClass()).get(); + Struct struct = StructFetcher.fetchStruct((Class) value.getClass()).get(); ByteBuffer buffer = ByteBuffer.allocate(struct.getSize()); buffer.order(ByteOrder.LITTLE_ENDIAN); struct.pack(buffer, value); @@ -123,8 +121,7 @@ private void testStructDoublePack(S value) { @SuppressWarnings("unchecked") private void testStructDoubleUnpack(S value) { - Struct struct = - ProceduralStructGenerator.extractClassStruct((Class) value.getClass()).get(); + Struct struct = StructFetcher.fetchStruct((Class) value.getClass()).get(); ByteBuffer buffer = ByteBuffer.allocate(struct.getSize()); buffer.order(ByteOrder.LITTLE_ENDIAN); struct.pack(buffer, value);