diff --git a/build.gradle.kts b/build.gradle.kts index 1ba916e..1f5940e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,4 @@ allprojects { - group = "de.exlll" - version = "4.5.0" + group = "org.inksnow.husk" + version = "4.5.1" } diff --git a/buildSrc/src/main/kotlin/core-config.gradle.kts b/buildSrc/src/main/kotlin/core-config.gradle.kts index 395442a..a6205e0 100644 --- a/buildSrc/src/main/kotlin/core-config.gradle.kts +++ b/buildSrc/src/main/kotlin/core-config.gradle.kts @@ -6,8 +6,8 @@ plugins { } java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 withJavadocJar() withSourcesJar() @@ -37,11 +37,19 @@ dependencies { publishing { repositories { maven { - name = "GitHubPackages" - url = uri("https://maven.pkg.github.com/Exlll/ConfigLib") + name = "husk-release" + url = findProperty("repository.huskrelease.url") + ?.let(::uri) + ?: throw GradleException("repository.huskrelease.url is not set") + credentials { - username = System.getenv("GITHUB_ACTOR") - password = System.getenv("GITHUB_TOKEN") + username = findProperty("repository.huskrelease.username") + ?.toString() + ?: throw GradleException("repository.huskrelease.username is not set") + + password = findProperty("repository.huskrelease.password") + ?.toString() + ?: throw GradleException("repository.huskrelease.password is not set") } } } diff --git a/configlib-core/src/main/java/de/exlll/configlib/CommentNode.java b/configlib-core/src/main/java/de/exlll/configlib/CommentNode.java index ec7e0fc..ed7ab56 100644 --- a/configlib-core/src/main/java/de/exlll/configlib/CommentNode.java +++ b/configlib-core/src/main/java/de/exlll/configlib/CommentNode.java @@ -24,9 +24,22 @@ import java.util.List; * int fn2; * } * - * - * @param comments - * @param elementNames */ -record CommentNode(List comments, List elementNames) {} +public class CommentNode { + private final List comments; + private final List elementNames; + + public CommentNode(List comments, List elementNames) { + this.comments = comments; + this.elementNames = elementNames; + } + + public List elementNames() { + return elementNames; + } + + public List comments() { + return comments; + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/CommentNodeExtractor.java b/configlib-core/src/main/java/de/exlll/configlib/CommentNodeExtractor.java index 355f54b..b28aeed 100644 --- a/configlib-core/src/main/java/de/exlll/configlib/CommentNodeExtractor.java +++ b/configlib-core/src/main/java/de/exlll/configlib/CommentNodeExtractor.java @@ -2,9 +2,11 @@ package de.exlll.configlib; import de.exlll.configlib.ConfigurationElements.FieldElement; import de.exlll.configlib.ConfigurationElements.RecordComponentElement; +import de.exlll.configlib.util.ref.R$Class; import java.lang.reflect.AnnotatedElement; import java.util.*; +import java.util.stream.Collectors; import static de.exlll.configlib.Validator.requireConfigurationType; import static de.exlll.configlib.Validator.requireNonNull; @@ -20,10 +22,23 @@ final class CommentNodeExtractor { this.outputNull = properties.outputNulls(); } - private record State( - Iterator> iterator, - Object elementHolder - ) {} + private class State { + private final Iterator> iterator; + private final Object elementHolder; + + private State(Iterator> iterator, Object elementHolder) { + this.iterator = iterator; + this.elementHolder = elementHolder; + } + + public Iterator> iterator() { + return iterator; + } + + public Object elementHolder() { + return elementHolder; + } + } /** * Extracts {@code CommentNode}s of the given configuration type in a DFS manner. @@ -37,8 +52,8 @@ final class CommentNodeExtractor { public Queue extractCommentNodes(final Object elementHolder) { requireConfigurationType(elementHolder.getClass()); final Queue result = new ArrayDeque<>(); - final var elementNameStack = new ArrayDeque<>(List.of("")); - final var stateStack = new ArrayDeque<>(List.of(stateFromObject(elementHolder))); + final ArrayDeque elementNameStack = new ArrayDeque<>(Collections.singletonList("")); + final ArrayDeque stateStack = new ArrayDeque<>(Collections.singletonList(stateFromObject(elementHolder))); State state; while (!stateStack.isEmpty()) { @@ -46,21 +61,21 @@ final class CommentNodeExtractor { elementNameStack.removeLast(); while (state.iterator.hasNext()) { - final var element = state.iterator.next(); - final var elementValue = element.value(state.elementHolder); + final ConfigurationElement element = state.iterator.next(); + final Object elementValue = element.value(state.elementHolder); if ((elementValue == null) && !outputNull) continue; - final var elementName = element.name(); - final var commentNode = createNodeIfCommentPresent( + final String elementName = element.name(); + final Optional commentNode = createNodeIfCommentPresent( element.element(), elementName, elementNameStack ); commentNode.ifPresent(result::add); - final var elementType = element.type(); + final Class elementType = element.type(); if ((elementValue != null) && Reflect.isConfigurationType(elementType)) { stateStack.addLast(state); elementNameStack.addLast(nameFormatter.format(elementName)); @@ -73,8 +88,8 @@ final class CommentNodeExtractor { } private State stateFromObject(final Object elementHolder) { - final var type = elementHolder.getClass(); - final var iter = type.isRecord() + final Class type = elementHolder.getClass(); + final Iterator> iter = R$Class.of(type).isRecord() ? recordComponentElements(elementHolder) : fieldElements(elementHolder); return new State(iter, elementHolder); @@ -86,13 +101,13 @@ final class CommentNodeExtractor { final Deque elementNameStack ) { if (element.isAnnotationPresent(Comment.class)) { - final var comments = Arrays.stream(element.getAnnotation(Comment.class).value()) + final List comments = Arrays.stream(element.getAnnotation(Comment.class).value()) .flatMap(s -> Arrays.stream(s.split("\n", -1))) - .toList(); - final var formattedName = nameFormatter.format(elementName); - final var elementNames = new ArrayList<>(elementNameStack); + .collect(Collectors.toList()); + final String formattedName = nameFormatter.format(elementName); + final ArrayList elementNames = new ArrayList<>(elementNameStack); elementNames.add(formattedName); - final var result = new CommentNode(comments, elementNames); + final CommentNode result = new CommentNode(comments, elementNames); return Optional.of(result); } return Optional.empty(); @@ -106,7 +121,7 @@ final class CommentNodeExtractor { } private Iterator recordComponentElements(Object record) { - return Arrays.stream(record.getClass().getRecordComponents()) + return Arrays.stream(R$Class.of(record.getClass()).getRecordComponents()) .map(RecordComponentElement::new) .iterator(); } diff --git a/configlib-core/src/main/java/de/exlll/configlib/ConfigurationElement.java b/configlib-core/src/main/java/de/exlll/configlib/ConfigurationElement.java index 72800f0..aa7d467 100644 --- a/configlib-core/src/main/java/de/exlll/configlib/ConfigurationElement.java +++ b/configlib-core/src/main/java/de/exlll/configlib/ConfigurationElement.java @@ -4,18 +4,14 @@ import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.AnnotatedType; import java.lang.reflect.Field; -import java.lang.reflect.RecordComponent; /** * Represents an element of a serializable configuration type. The element can either be a - * {@link Field} for configuration classes or a {@link RecordComponent} for records. + * {@link Field} for configuration classes or a {@link java.lang.reflect.RecordComponent} for records. * * @param the type of the element */ -public sealed interface ConfigurationElement - permits - ConfigurationElements.FieldElement, - ConfigurationElements.RecordComponentElement { +public interface ConfigurationElement { /** * Returns the element itself. * diff --git a/configlib-core/src/main/java/de/exlll/configlib/ConfigurationElements.java b/configlib-core/src/main/java/de/exlll/configlib/ConfigurationElements.java index 7cbec3d..d9579fb 100644 --- a/configlib-core/src/main/java/de/exlll/configlib/ConfigurationElements.java +++ b/configlib-core/src/main/java/de/exlll/configlib/ConfigurationElements.java @@ -1,13 +1,25 @@ package de.exlll.configlib; +import de.exlll.configlib.util.ref.R$RecordComponent; + import java.lang.reflect.AnnotatedType; import java.lang.reflect.Field; -import java.lang.reflect.RecordComponent; final class ConfigurationElements { private ConfigurationElements() {} - record FieldElement(Field element) implements ConfigurationElement { + static class FieldElement implements ConfigurationElement { + private final Field element; + + public FieldElement(Field element) { + this.element = element; + } + + @Override + public Field element() { + return element; + } + @Override public String name() { return element.getName(); @@ -34,8 +46,18 @@ final class ConfigurationElements { } } - record RecordComponentElement(RecordComponent element) - implements ConfigurationElement { + static class RecordComponentElement implements ConfigurationElement { + private final R$RecordComponent element; + + RecordComponentElement(R$RecordComponent element) { + this.element = element; + } + + @Override + public R$RecordComponent element() { + return element; + } + @Override public String name() { return element.getName(); diff --git a/configlib-core/src/main/java/de/exlll/configlib/ConfigurationSerializer.java b/configlib-core/src/main/java/de/exlll/configlib/ConfigurationSerializer.java index b1ac4ab..6f99710 100644 --- a/configlib-core/src/main/java/de/exlll/configlib/ConfigurationSerializer.java +++ b/configlib-core/src/main/java/de/exlll/configlib/ConfigurationSerializer.java @@ -4,6 +4,7 @@ import de.exlll.configlib.ConfigurationElements.FieldElement; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; final class ConfigurationSerializer extends TypeSerializer { ConfigurationSerializer(Class configurationType, ConfigurationProperties properties) { @@ -12,8 +13,8 @@ final class ConfigurationSerializer extends TypeSerializer { @Override public T deserialize(Map serializedConfiguration) { - final var deserializedElements = deserializeConfigurationElements(serializedConfiguration); - final var elements = elements(); + final Object[] deserializedElements = deserializeConfigurationElements(serializedConfiguration); + final List elements = elements(); final T result = newDefaultInstance(); for (int i = 0; i < deserializedElements.length; i++) { final FieldElement fieldElement = elements.get(i); @@ -33,8 +34,7 @@ final class ConfigurationSerializer extends TypeSerializer { @Override protected String baseDeserializeExceptionMessage(FieldElement element, Object value) { - return "Deserialization of value '%s' with type '%s' for field '%s' failed." - .formatted(value, value.getClass(), element.element()); + return "Deserialization of value '" + value + "' with type '" + value.getClass() + "' for field '" + element.element() + "' failed."; } @Override @@ -42,7 +42,7 @@ final class ConfigurationSerializer extends TypeSerializer { return FieldExtractors.CONFIGURATION.extract(type) .filter(properties.getFieldFilter()) .map(FieldElement::new) - .toList(); + .collect(Collectors.toList()); } @Override diff --git a/configlib-core/src/main/java/de/exlll/configlib/FieldExtractors.java b/configlib-core/src/main/java/de/exlll/configlib/FieldExtractors.java index 5bb4382..cee9e3d 100644 --- a/configlib-core/src/main/java/de/exlll/configlib/FieldExtractors.java +++ b/configlib-core/src/main/java/de/exlll/configlib/FieldExtractors.java @@ -3,6 +3,7 @@ package de.exlll.configlib; import java.lang.reflect.Field; import java.util.*; import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.stream.Stream; enum FieldExtractors implements FieldExtractor { @@ -22,7 +23,7 @@ enum FieldExtractors implements FieldExtractor { List fields = classes.stream() .flatMap(c -> Arrays.stream(c.getDeclaredFields())) .filter(FieldFilters.DEFAULT) - .toList(); + .collect(Collectors.toList()); requireNoShadowing(fields); return fields.stream(); } @@ -32,8 +33,8 @@ enum FieldExtractors implements FieldExtractor { Map> map = new LinkedHashMap<>(); for (Field field : fields) { - var fieldName = field.getName(); - var fieldClass = field.getDeclaringClass(); + String fieldName = field.getName(); + Class fieldClass = field.getDeclaringClass(); if (map.containsKey(fieldName)) { Class superClass = map.get(fieldName); diff --git a/configlib-core/src/main/java/de/exlll/configlib/PolymorphicSerializer.java b/configlib-core/src/main/java/de/exlll/configlib/PolymorphicSerializer.java index bd75308..6bea44c 100644 --- a/configlib-core/src/main/java/de/exlll/configlib/PolymorphicSerializer.java +++ b/configlib-core/src/main/java/de/exlll/configlib/PolymorphicSerializer.java @@ -1,5 +1,7 @@ package de.exlll.configlib; +import de.exlll.configlib.util.StringUtil; + import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @@ -21,22 +23,20 @@ final class PolymorphicSerializer implements Serializer> { } private void requireNonBlankProperty() { - if (polymorphic.property().isBlank()) { - String msg = "The @Polymorphic annotation does not allow a blank property name but " + - "type '%s' uses one.".formatted(polymorphicType.getName()); - throw new ConfigurationException(msg); + if (StringUtil.isBlank(polymorphic.property())) { + throw new ConfigurationException("The @Polymorphic annotation does not allow a blank property name but type '" + polymorphicType.getName() + "' uses one."); } } private void initAliases() { - final var polymorphicTypes = polymorphicType.getAnnotation(PolymorphicTypes.class); + final PolymorphicTypes polymorphicTypes = polymorphicType.getAnnotation(PolymorphicTypes.class); if (polymorphicTypes == null) return; for (PolymorphicTypes.Type pType : polymorphicTypes.value()) { - final var type = pType.type(); - final var alias = pType.alias().isBlank() ? type.getName() : pType.alias(); + final Class type = pType.type(); + final String alias = StringUtil.isBlank(pType.alias()) ? type.getName() : pType.alias(); requireDistinctAliases(alias); requireDistinctTypes(type); @@ -48,37 +48,32 @@ final class PolymorphicSerializer implements Serializer> { private void requireDistinctAliases(String alias) { if (typeByAlias.containsKey(alias)) { - String msg = "The @PolymorphicTypes annotation must not use the same alias for " + - "multiple types. Alias '%s' appears more than once." - .formatted(alias); - throw new ConfigurationException(msg); + throw new ConfigurationException("The @PolymorphicTypes annotation must not use the same alias for multiple types. Alias '" + alias + "' appears more than once."); } } private void requireDistinctTypes(Class type) { if (aliasByType.containsKey(type)) { - String msg = "The @PolymorphicTypes annotation must not contain multiple " + - "definitions for the same subtype. Type '%s' appears more than once." - .formatted(type.getName()); - throw new ConfigurationException(msg); + throw new ConfigurationException("The @PolymorphicTypes annotation must not contain multiple definitions for the same subtype. Type '" + type.getName() + "' appears more than once."); } } @Override + @SuppressWarnings("rawtypes") public Map serialize(Object element) { final Class elementType = element.getClass(); // this cast won't cause any exceptions as we only pass objects of types the // serializer expects @SuppressWarnings("unchecked") - final var serializer = (TypeSerializer) TypeSerializer.newSerializerFor( + final TypeSerializer serializer = (TypeSerializer) TypeSerializer.newSerializerFor( elementType, context.properties() ); - final var serialization = serializer.serialize(element); + final Map serialization = serializer.serialize(element); requireSerializationNotContainsProperty(serialization); - final var result = new LinkedHashMap<>(); + final LinkedHashMap result = new LinkedHashMap<>(); result.put(polymorphic.property(), getTypeIdentifierByType(elementType)); result.putAll(serialization); return result; @@ -91,11 +86,9 @@ final class PolymorphicSerializer implements Serializer> { private void requireSerializationNotContainsProperty(Map serialization) { if (serialization.containsKey(polymorphic.property())) { - String msg = ("Polymorphic serialization for type '%s' failed. The type contains a " + - "configuration element with name '%s' but that name is " + - "used by the @Polymorphic property.") - .formatted(polymorphicType.getName(), polymorphic.property()); - throw new ConfigurationException(msg); + throw new ConfigurationException("Polymorphic serialization for type '" + polymorphicType.getName() + + "' failed. The type contains a configuration element with name '" + polymorphic.property() + + "' but that name is used by the @Polymorphic property."); } } @@ -103,11 +96,11 @@ final class PolymorphicSerializer implements Serializer> { public Object deserialize(Map element) { requirePropertyPresent(element); - final var typeIdentifier = element.get(polymorphic.property()); + final Object typeIdentifier = element.get(polymorphic.property()); requireTypeIdentifierString(typeIdentifier); - final var type = getTypeByTypeIdentifier((String) typeIdentifier); - final var serializer = TypeSerializer.newSerializerFor(type, context.properties()); + final Class type = getTypeByTypeIdentifier((String) typeIdentifier); + final TypeSerializer serializer = TypeSerializer.newSerializerFor(type, context.properties()); return serializer.deserialize(element); } @@ -120,41 +113,23 @@ final class PolymorphicSerializer implements Serializer> { try { return Reflect.getClassByName(className); } catch (RuntimeException e) { - String msg = ("Polymorphic deserialization for type '%s' failed. " + - "The class '%s' does not exist.") - .formatted(polymorphicType.getName(), className); - throw new ConfigurationException(msg, e); + throw new ConfigurationException("Polymorphic deserialization for type '" + polymorphicType.getName() + "' failed. The class '" + className + "' does not exist.", e); } } private void requirePropertyPresent(Map element) { if (element.get(polymorphic.property()) != null) return; - String msg = """ - Polymorphic deserialization for type '%s' failed. \ - The property '%s' which holds the type is missing. \ - Value to be deserialized: - %s\ - """ - .formatted( - polymorphicType.getName(), - polymorphic.property(), - element - ); - throw new ConfigurationException(msg); + throw new ConfigurationException("Polymorphic deserialization for type '" + polymorphicType.getName() + + "' failed. The property '" + polymorphic.property() + "' which holds the type is missing. " + + "Value to be deserialized:\n" + element); } private void requireTypeIdentifierString(Object typeIdentifier) { if (typeIdentifier instanceof String) return; - String msg = ("Polymorphic deserialization for type '%s' failed. The type identifier '%s' " + - "which should hold the type is not a string but of type '%s'.") - .formatted( - polymorphicType.getName(), - typeIdentifier, - typeIdentifier.getClass().getName() - ); - throw new ConfigurationException(msg); + throw new ConfigurationException("Polymorphic deserialization for type '" + polymorphicType.getName() + "' failed. The type identifier '" + typeIdentifier + "' " + + "which should hold the type is not a string but of type '" + typeIdentifier.getClass().getName() + "'."); } Class getPolymorphicType() { diff --git a/configlib-core/src/main/java/de/exlll/configlib/RecordSerializer.java b/configlib-core/src/main/java/de/exlll/configlib/RecordSerializer.java index 57839c6..420ed75 100644 --- a/configlib-core/src/main/java/de/exlll/configlib/RecordSerializer.java +++ b/configlib-core/src/main/java/de/exlll/configlib/RecordSerializer.java @@ -1,10 +1,12 @@ package de.exlll.configlib; import de.exlll.configlib.ConfigurationElements.RecordComponentElement; +import de.exlll.configlib.util.ref.R$Class; import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; final class RecordSerializer extends TypeSerializer { RecordSerializer(Class recordType, ConfigurationProperties properties) { @@ -13,31 +15,28 @@ final class RecordSerializer extends TypeSerializer serializedConfiguration) { - final var ctorArgs = deserializeConfigurationElements(serializedConfiguration); - final var result = Reflect.callCanonicalConstructor(type, ctorArgs); + final Object[] ctorArgs = deserializeConfigurationElements(serializedConfiguration); + final R result = Reflect.callCanonicalConstructor(type, ctorArgs); return postProcessor.apply(result); } @Override protected void requireSerializableElements() { if (serializers.isEmpty()) { - String msg = "Record type '%s' does not define any components." - .formatted(type.getSimpleName()); - throw new ConfigurationException(msg); + throw new ConfigurationException("Record type '" + type.getSimpleName() + "' does not define any components."); } } @Override protected String baseDeserializeExceptionMessage(RecordComponentElement element, Object value) { - return "Deserialization of value '%s' with type '%s' for component '%s' of record '%s' failed." - .formatted(value, value.getClass(), element.element(), element.declaringType()); + return "Deserialization of value '" + value + "' with type '" + value.getClass() + "' for component '" + element.element() + "' of record '" + element.declaringType() + "' failed."; } @Override protected List elements() { - return Arrays.stream(type.getRecordComponents()) + return Arrays.stream(R$Class.of(type).getRecordComponents()) .map(RecordComponentElement::new) - .toList(); + .collect(Collectors.toList()); } @Override diff --git a/configlib-core/src/main/java/de/exlll/configlib/Reflect.java b/configlib-core/src/main/java/de/exlll/configlib/Reflect.java index f89b2eb..4a22b18 100644 --- a/configlib-core/src/main/java/de/exlll/configlib/Reflect.java +++ b/configlib-core/src/main/java/de/exlll/configlib/Reflect.java @@ -1,5 +1,8 @@ package de.exlll.configlib; +import de.exlll.configlib.util.ref.R$Class; +import de.exlll.configlib.util.ref.R$RecordComponent; + import java.lang.reflect.*; import java.util.Arrays; import java.util.List; @@ -45,19 +48,14 @@ final class Reflect { constructor.setAccessible(true); return constructor.newInstance(arguments); } catch (NoSuchMethodException e) { - String msg = "Type '%s' doesn't have a constructor with parameters: %s." - .formatted(cls.getSimpleName(), argumentTypeNamesJoined(argumentTypes)); - throw new RuntimeException(msg, e); + throw new RuntimeException("Type '" + cls.getSimpleName() + "' doesn't have a constructor with parameters: " + argumentTypeNamesJoined(argumentTypes) + ".", e); } catch (IllegalAccessException e) { // cannot happen because we set the constructor to be accessible. throw new RuntimeException(e); } catch (InstantiationException e) { - String msg = "Type '%s' is not instantiable.".formatted(cls.getSimpleName()); - throw new RuntimeException(msg, e); + throw new RuntimeException("Type '" + cls.getSimpleName() + "' is not instantiable.", e); } catch (InvocationTargetException e) { - String msg = "Constructor of type '%s' with parameters '%s' threw an exception." - .formatted(cls.getSimpleName(), argumentTypeNamesJoined(argumentTypes)); - throw new RuntimeException(msg, e); + throw new RuntimeException("Constructor of type '" + cls.getSimpleName() + "' with parameters '" + argumentTypeNamesJoined(argumentTypes) + "' threw an exception.", e); } } @@ -79,22 +77,15 @@ final class Reflect { constructor.setAccessible(true); return constructor.newInstance(); } catch (NoSuchMethodException e) { - String msg = "Type '%s' doesn't have a no-args constructor." - .formatted(cls.getSimpleName()); - throw new RuntimeException(msg, e); + throw new RuntimeException("Type '" + cls.getSimpleName() + "' doesn't have a no-args constructor.", e); } catch (IllegalAccessException e) { /* This exception should not be thrown because * we set the constructor to be accessible. */ - String msg = "No-args constructor of type '%s' not accessible." - .formatted(cls.getSimpleName()); - throw new RuntimeException(msg, e); + throw new RuntimeException("No-args constructor of type '" + cls.getSimpleName() + "' not accessible.", e); } catch (InstantiationException e) { - String msg = "Type '%s' is not instantiable.".formatted(cls.getSimpleName()); - throw new RuntimeException(msg, e); + throw new RuntimeException("Type '" + cls.getSimpleName() + "' is not instantiable.", e); } catch (InvocationTargetException e) { - String msg = "No-args constructor of type '%s' threw an exception." - .formatted(cls.getSimpleName()); - throw new RuntimeException(msg, e); + throw new RuntimeException("No-args constructor of type '" + cls.getSimpleName() + "' threw an exception.", e); } } @@ -115,9 +106,7 @@ final class Reflect { // cannot happen because records are instantiable throw new RuntimeException(e); } catch (InvocationTargetException e) { - String msg = "The canonical constructor of record type '%s' threw an exception." - .formatted(recordType.getSimpleName()); - throw new RuntimeException(msg, e); + throw new RuntimeException("The canonical constructor of record type '" + recordType.getSimpleName() + "' threw an exception.", e); } } @@ -135,8 +124,8 @@ final class Reflect { } private static Class[] recordParameterTypes(Class recordType) { - return Arrays.stream(recordType.getRecordComponents()) - .map(RecordComponent::getType) + return Arrays.stream(R$Class.of(recordType).getRecordComponents()) + .map(R$RecordComponent::getType) .toArray(Class[]::new); } @@ -158,25 +147,20 @@ final class Reflect { return field.get(instance); } catch (IllegalAccessException e) { /* This exception should not be thrown because we set the field to be accessible. */ - String msg = "Illegal access of field '%s' on object %s.".formatted(field, instance); - throw new RuntimeException(msg, e); + throw new RuntimeException("Illegal access of field '" + field + "' on object " + instance + ".", e); } } - static Object getValue(RecordComponent component, Object recordInstance) { + static Object getValue(R$RecordComponent component, Object recordInstance) { final Method accessor = component.getAccessor(); try { accessor.setAccessible(true); return accessor.invoke(recordInstance); } catch (IllegalAccessException e) { /* Should not be thrown because we set the method to be accessible. */ - String msg = "Illegal access of method '%s' on record '%s'." - .formatted(accessor, recordInstance); - throw new RuntimeException(msg, e); + throw new RuntimeException("Illegal access of method '" + accessor + "' on record '" + recordInstance + "'.", e); } catch (InvocationTargetException e) { - String msg = "Invocation of method '%s' on record '%s' failed." - .formatted(accessor, recordInstance); - throw new RuntimeException(msg, e); + throw new RuntimeException("Invocation of method '" + accessor + "' on record '" + recordInstance + "' failed.", e); } } @@ -186,8 +170,7 @@ final class Reflect { field.set(instance, value); } catch (IllegalAccessException e) { /* This exception should not be thrown because we set the field to be accessible. */ - String msg = "Illegal access of field '%s' on object %s.".formatted(field, instance); - throw new RuntimeException(msg, e); + throw new RuntimeException("Illegal access of field '" + field + "' on object " + instance + ".", e); } } @@ -228,7 +211,7 @@ final class Reflect { } static boolean isConfigurationType(Class type) { - return type.isRecord() || (type.getAnnotation(Configuration.class) != null); + return R$Class.of(type).isRecord() || (type.getAnnotation(Configuration.class) != null); } static boolean isIgnored(Field field) { diff --git a/configlib-core/src/main/java/de/exlll/configlib/SerializerContextImpl.java b/configlib-core/src/main/java/de/exlll/configlib/SerializerContextImpl.java index 14bf8ac..dae8e12 100644 --- a/configlib-core/src/main/java/de/exlll/configlib/SerializerContextImpl.java +++ b/configlib-core/src/main/java/de/exlll/configlib/SerializerContextImpl.java @@ -4,15 +4,30 @@ import java.lang.reflect.AnnotatedType; import static de.exlll.configlib.Validator.requireNonNull; -record SerializerContextImpl( - ConfigurationProperties properties, - ConfigurationElement element, - AnnotatedType annotatedType -) implements SerializerContext { - SerializerContextImpl { - properties = requireNonNull(properties, "configuration properties"); - element = requireNonNull(element, "configuration element"); - annotatedType = requireNonNull(annotatedType, "annotated type"); +class SerializerContextImpl implements SerializerContext { + private final ConfigurationProperties properties; + private final ConfigurationElement element; + private final AnnotatedType annotatedType; + + SerializerContextImpl(ConfigurationProperties properties, ConfigurationElement element, AnnotatedType annotatedType) { + this.properties = requireNonNull(properties, "configuration properties"); + this.element = requireNonNull(element, "configuration element"); + this.annotatedType = requireNonNull(annotatedType, "annotated type"); + } + + @Override + public ConfigurationProperties properties() { + return properties; + } + + @Override + public ConfigurationElement element() { + return element; + } + + @Override + public AnnotatedType annotatedType() { + return annotatedType; } } diff --git a/configlib-core/src/main/java/de/exlll/configlib/SerializerSelector.java b/configlib-core/src/main/java/de/exlll/configlib/SerializerSelector.java index fdef927..2b5bc33 100644 --- a/configlib-core/src/main/java/de/exlll/configlib/SerializerSelector.java +++ b/configlib-core/src/main/java/de/exlll/configlib/SerializerSelector.java @@ -1,8 +1,10 @@ package de.exlll.configlib; import de.exlll.configlib.Serializers.*; +import de.exlll.configlib.util.MapUtil; import java.io.File; +import java.lang.annotation.Annotation; import java.lang.reflect.*; import java.math.BigDecimal; import java.math.BigInteger; @@ -16,39 +18,41 @@ import java.time.LocalTime; import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.function.Function; +import java.util.function.Predicate; import static de.exlll.configlib.Validator.requireNonNull; final class SerializerSelector { private static final Map, Serializer> DEFAULT_SERIALIZERS = Map.ofEntries( - Map.entry(boolean.class, new BooleanSerializer()), - Map.entry(Boolean.class, new BooleanSerializer()), - Map.entry(byte.class, new NumberSerializer(byte.class)), - Map.entry(Byte.class, new NumberSerializer(Byte.class)), - Map.entry(short.class, new NumberSerializer(short.class)), - Map.entry(Short.class, new NumberSerializer(Short.class)), - Map.entry(int.class, new NumberSerializer(int.class)), - Map.entry(Integer.class, new NumberSerializer(Integer.class)), - Map.entry(long.class, new NumberSerializer(long.class)), - Map.entry(Long.class, new NumberSerializer(Long.class)), - Map.entry(float.class, new NumberSerializer(float.class)), - Map.entry(Float.class, new NumberSerializer(Float.class)), - Map.entry(double.class, new NumberSerializer(double.class)), - Map.entry(Double.class, new NumberSerializer(Double.class)), - Map.entry(char.class, new CharacterSerializer()), - Map.entry(Character.class, new CharacterSerializer()), - Map.entry(String.class, new StringSerializer()), - Map.entry(BigInteger.class, new BigIntegerSerializer()), - Map.entry(BigDecimal.class, new BigDecimalSerializer()), - Map.entry(LocalDate.class, new LocalDateSerializer()), - Map.entry(LocalTime.class, new LocalTimeSerializer()), - Map.entry(LocalDateTime.class, new LocalDateTimeSerializer()), - Map.entry(Instant.class, new InstantSerializer()), - Map.entry(UUID.class, new UuidSerializer()), - Map.entry(File.class, new FileSerializer()), - Map.entry(Path.class, new PathSerializer()), - Map.entry(URL.class, new UrlSerializer()), - Map.entry(URI.class, new UriSerializer()) + MapUtil.entry(boolean.class, new BooleanSerializer()), + MapUtil.entry(Boolean.class, new BooleanSerializer()), + MapUtil.entry(byte.class, new NumberSerializer(byte.class)), + MapUtil.entry(Byte.class, new NumberSerializer(Byte.class)), + MapUtil.entry(short.class, new NumberSerializer(short.class)), + MapUtil.entry(Short.class, new NumberSerializer(Short.class)), + MapUtil.entry(int.class, new NumberSerializer(int.class)), + MapUtil.entry(Integer.class, new NumberSerializer(Integer.class)), + MapUtil.entry(long.class, new NumberSerializer(long.class)), + MapUtil.entry(Long.class, new NumberSerializer(Long.class)), + MapUtil.entry(float.class, new NumberSerializer(float.class)), + MapUtil.entry(Float.class, new NumberSerializer(Float.class)), + MapUtil.entry(double.class, new NumberSerializer(double.class)), + MapUtil.entry(Double.class, new NumberSerializer(Double.class)), + MapUtil.entry(char.class, new CharacterSerializer()), + MapUtil.entry(Character.class, new CharacterSerializer()), + MapUtil.entry(String.class, new StringSerializer()), + MapUtil.entry(BigInteger.class, new BigIntegerSerializer()), + MapUtil.entry(BigDecimal.class, new BigDecimalSerializer()), + MapUtil.entry(LocalDate.class, new LocalDateSerializer()), + MapUtil.entry(LocalTime.class, new LocalTimeSerializer()), + MapUtil.entry(LocalDateTime.class, new LocalDateTimeSerializer()), + MapUtil.entry(Instant.class, new InstantSerializer()), + MapUtil.entry(UUID.class, new UuidSerializer()), + MapUtil.entry(File.class, new FileSerializer()), + MapUtil.entry(Path.class, new PathSerializer()), + MapUtil.entry(URL.class, new UrlSerializer()), + MapUtil.entry(URI.class, new UriSerializer()) ); private final ConfigurationProperties properties; /** @@ -101,18 +105,28 @@ final class SerializerSelector { } private Serializer selectCustomSerializer(AnnotatedType annotatedType) { - return findConfigurationElementSerializer(annotatedType) - .or(() -> findSerializerFactoryForType(annotatedType)) - .or(() -> findSerializerForType(annotatedType)) - .or(() -> findSerializerOnType(annotatedType)) - .or(() -> findMetaSerializerOnType(annotatedType)) - .or(() -> findSerializerByCondition(annotatedType)) - .orElse(null); + Optional> o = findConfigurationElementSerializer(annotatedType); + if (o.isPresent()) return o.get(); + + o = findSerializerFactoryForType(annotatedType); + if (o.isPresent()) return o.get(); + + o = findSerializerForType(annotatedType); + if (o.isPresent()) return o.get(); + + o = findSerializerOnType(annotatedType); + if (o.isPresent()) return o.get(); + + o = findMetaSerializerOnType(annotatedType); + if (o.isPresent()) return o.get(); + + o = findSerializerByCondition(annotatedType); + return o.orElse(null); } private Optional> findConfigurationElementSerializer(AnnotatedType annotatedType) { // SerializeWith annotation on configuration elements - final var annotation = element.annotation(SerializeWith.class); + final SerializeWith annotation = element.annotation(SerializeWith.class); if ((annotation != null) && (currentNesting == annotation.nesting())) { return Optional.of(newSerializerFromAnnotation(annotatedType, annotation)); } @@ -121,11 +135,12 @@ final class SerializerSelector { private Optional> findSerializerFactoryForType(AnnotatedType annotatedType) { // Serializer factory registered for Type via configurations properties - if ((annotatedType.getType() instanceof Class cls) && - properties.getSerializerFactories().containsKey(cls)) { - final var context = new SerializerContextImpl(properties, element, annotatedType); - final var factory = properties.getSerializerFactories().get(cls); - final var serializer = factory.apply(context); + if ((annotatedType.getType() instanceof Class) && + properties.getSerializerFactories().containsKey((Class) annotatedType.getType())) { + Class cls = (Class) annotatedType.getType(); + final SerializerContextImpl context = new SerializerContextImpl(properties, element, annotatedType); + final Function> factory = properties.getSerializerFactories().get(cls); + final Serializer serializer = factory.apply(context); if (serializer == null) { String msg = "Serializer factories must not return null."; throw new ConfigurationException(msg); @@ -137,8 +152,9 @@ final class SerializerSelector { private Optional> findSerializerForType(AnnotatedType annotatedType) { // Serializer registered for Type via configurations properties - if ((annotatedType.getType() instanceof Class cls) && - properties.getSerializers().containsKey(cls)) { + if ((annotatedType.getType() instanceof Class) && + properties.getSerializers().containsKey((Class) annotatedType.getType())) { + Class cls = (Class) annotatedType.getType(); return Optional.of(properties.getSerializers().get(cls)); } return Optional.empty(); @@ -146,9 +162,10 @@ final class SerializerSelector { private Optional> findSerializerOnType(AnnotatedType annotatedType) { // SerializeWith annotation on type - if ((annotatedType.getType() instanceof Class cls) && - (cls.getDeclaredAnnotation(SerializeWith.class) != null)) { - final var annotation = cls.getDeclaredAnnotation(SerializeWith.class); + if ((annotatedType.getType() instanceof Class) && + (((Class) annotatedType.getType()).getDeclaredAnnotation(SerializeWith.class) != null)) { + final Class cls = (Class) annotatedType.getType(); + final SerializeWith annotation = cls.getDeclaredAnnotation(SerializeWith.class); return Optional.of(newSerializerFromAnnotation(annotatedType, annotation)); } return Optional.empty(); @@ -156,10 +173,11 @@ final class SerializerSelector { private Optional> findMetaSerializerOnType(AnnotatedType annotatedType) { // SerializeWith meta annotation on type - if ((annotatedType.getType() instanceof Class cls)) { - for (final var meta : cls.getDeclaredAnnotations()) { - final var metaType = meta.annotationType(); - final var annotation = metaType.getDeclaredAnnotation(SerializeWith.class); + if (annotatedType.getType() instanceof Class) { + final Class cls = (Class) annotatedType.getType(); + for (final Annotation meta : cls.getDeclaredAnnotations()) { + final Class metaType = meta.annotationType(); + final SerializeWith annotation = metaType.getDeclaredAnnotation(SerializeWith.class); if (annotation != null) return Optional.of(newSerializerFromAnnotation(annotatedType, annotation)); } @@ -169,7 +187,7 @@ final class SerializerSelector { private Optional> findSerializerByCondition(AnnotatedType annotatedType) { // Serializer registered for condition via configurations properties - for (var entry : properties.getSerializersByCondition().entrySet()) { + for (Map.Entry, Serializer> entry : properties.getSerializersByCondition().entrySet()) { if (entry.getKey().test(annotatedType.getType())) return Optional.of(entry.getValue()); } @@ -180,7 +198,7 @@ final class SerializerSelector { AnnotatedType annotatedType, SerializeWith annotation ) { - final var context = new SerializerContextImpl(properties, element, annotatedType); + final SerializerContextImpl context = new SerializerContextImpl(properties, element, annotatedType); return Serializers.newCustomSerializer(annotation.serializer(), context); } @@ -191,7 +209,7 @@ final class SerializerSelector { if (Reflect.isEnumType(cls)) { // The following cast won't fail because we just checked that it's an enum. @SuppressWarnings("unchecked") - final var enumType = (Class>) cls; + final Class> enumType = (Class>) cls; return new Serializers.EnumSerializer(enumType); } if (Reflect.isArrayType(cls)) @@ -225,34 +243,34 @@ final class SerializerSelector { } else if (elementType == double.class) { return new PrimitiveDoubleArraySerializer(); } - var elementSerializer = selectForType(annotatedElementType); - var inputNulls = properties.inputNulls(); - var outputNulls = properties.outputNulls(); + Serializer elementSerializer = selectForType(annotatedElementType); + boolean inputNulls = properties.inputNulls(); + boolean outputNulls = properties.outputNulls(); return new ArraySerializer<>(elementType, elementSerializer, outputNulls, inputNulls); } private Serializer selectForParameterizedType(AnnotatedParameterizedType annotatedType) { // the raw type returned by Java is always a class - final var type = (ParameterizedType) annotatedType.getType(); - final var rawType = (Class) type.getRawType(); - final var typeArgs = annotatedType.getAnnotatedActualTypeArguments(); - final var inputNulls = properties.inputNulls(); - final var outputNulls = properties.outputNulls(); + final ParameterizedType type = (ParameterizedType) annotatedType.getType(); + final Class rawType = (Class) type.getRawType(); + final AnnotatedType[] typeArgs = annotatedType.getAnnotatedActualTypeArguments(); + final boolean inputNulls = properties.inputNulls(); + final boolean outputNulls = properties.outputNulls(); if (Reflect.isListType(rawType)) { - var elementSerializer = selectForType(typeArgs[0]); + Serializer elementSerializer = selectForType(typeArgs[0]); return new ListSerializer<>(elementSerializer, outputNulls, inputNulls); } else if (Reflect.isSetType(rawType)) { - var elementSerializer = selectForType(typeArgs[0]); + Serializer elementSerializer = selectForType(typeArgs[0]); return properties.serializeSetsAsLists() ? new SetAsListSerializer<>(elementSerializer, outputNulls, inputNulls) : new SetSerializer<>(elementSerializer, outputNulls, inputNulls); } else if (Reflect.isMapType(rawType)) { - if ((typeArgs[0].getType() instanceof Class cls) && - (DEFAULT_SERIALIZERS.containsKey(cls) || - Reflect.isEnumType(cls))) { - var keySerializer = selectForClass(typeArgs[0]); - var valSerializer = selectForType(typeArgs[1]); + if ((typeArgs[0].getType() instanceof Class) && + (DEFAULT_SERIALIZERS.containsKey((Class) typeArgs[0].getType()) || + Reflect.isEnumType((Class) typeArgs[0].getType()))) { + Serializer keySerializer = selectForClass(typeArgs[0]); + Serializer valSerializer = selectForType(typeArgs[1]); return new MapSerializer<>(keySerializer, valSerializer, outputNulls, inputNulls); } String msg = baseExceptionMessage(type) + @@ -266,6 +284,6 @@ final class SerializerSelector { } private String baseExceptionMessage(Type type) { - return "Cannot select serializer for type '%s'.\n".formatted(type); + return "Cannot select serializer for type '" + type + "'.\n"; } } diff --git a/configlib-core/src/main/java/de/exlll/configlib/Serializers.java b/configlib-core/src/main/java/de/exlll/configlib/Serializers.java index 69f49f1..bc19e08 100644 --- a/configlib-core/src/main/java/de/exlll/configlib/Serializers.java +++ b/configlib-core/src/main/java/de/exlll/configlib/Serializers.java @@ -7,6 +7,7 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.nio.file.Path; +import java.nio.file.Paths; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; @@ -300,7 +301,7 @@ final class Serializers { @Override public Path deserialize(String element) { - return Path.of(element); + return Paths.get(element); } } @@ -454,10 +455,10 @@ final class Serializers { for (final Map.Entry entry : element.entrySet()) { if (!outputNulls && isEntryNull(entry)) continue; - var s1key = entry.getKey(); - var s2val = entry.getValue(); - var t1key = (s1key == null) ? null : keySerializer.serialize(s1key); - var t2val = (s2val == null) ? null : valSerializer.serialize(s2val); + S1 s1key = entry.getKey(); + S2 s2val = entry.getValue(); + T1 t1key = (s1key == null) ? null : keySerializer.serialize(s1key); + T2 t2val = (s2val == null) ? null : valSerializer.serialize(s2val); result.put(t1key, t2val); } return result; @@ -470,10 +471,10 @@ final class Serializers { for (final Map.Entry entry : element.entrySet()) { if (!inputNulls && isEntryNull(entry)) continue; - var t1key = entry.getKey(); - var t2val = entry.getValue(); - var s1key = (t1key == null) ? null : keySerializer.deserialize(t1key); - var s2val = (t2val == null) ? null : valSerializer.deserialize(t2val); + T1 t1key = entry.getKey(); + T2 t2val = entry.getValue(); + S1 s1key = (t1key == null) ? null : keySerializer.deserialize(t1key); + S2 s2val = (t2val == null) ? null : valSerializer.deserialize(t2val); result.put(s1key, s2val); } return result; @@ -515,7 +516,7 @@ final class Serializers { final Stream stream = outputNulls ? Arrays.stream(element).map(s -> s == null ? null : serializer.serialize(s)) : Arrays.stream(element).filter(Objects::nonNull).map(serializer::serialize); - return stream.toList(); + return stream.collect(Collectors.toList()); } @Override @@ -543,7 +544,8 @@ final class Serializers { @Override public List serialize(Object element) { final boolean[] array = (boolean[]) element; - return IntStream.range(0, array.length).mapToObj(i -> array[i]).toList(); + return IntStream.range(0, array.length).mapToObj(i -> array[i]) + .collect(Collectors.toList()); } @Override @@ -564,7 +566,7 @@ final class Serializers { final char[] array = (char[]) element; return IntStream.range(0, array.length) .mapToObj(i -> serializer.serialize(array[i])) - .toList(); + .collect(Collectors.toList()); } @Override @@ -587,7 +589,7 @@ final class Serializers { final byte[] array = (byte[]) element; return IntStream.range(0, array.length). mapToObj(i -> serializer.serialize(array[i])) - .toList(); + .collect(Collectors.toList()); } @Override @@ -610,7 +612,7 @@ final class Serializers { final short[] array = (short[]) element; return IntStream.range(0, array.length) .mapToObj(i -> serializer.serialize(array[i])) - .toList(); + .collect(Collectors.toList()); } @Override @@ -631,7 +633,7 @@ final class Serializers { @Override public List serialize(Object element) { final int[] array = (int[]) element; - return Arrays.stream(array).mapToObj(serializer::serialize).toList(); + return Arrays.stream(array).mapToObj(serializer::serialize).collect(Collectors.toList()); } @Override @@ -652,7 +654,7 @@ final class Serializers { @Override public List serialize(Object element) { final long[] array = (long[]) element; - return Arrays.stream(array).mapToObj(serializer::serialize).toList(); + return Arrays.stream(array).mapToObj(serializer::serialize).collect(Collectors.toList()); } @Override @@ -675,7 +677,7 @@ final class Serializers { final float[] array = (float[]) element; return IntStream.range(0, array.length) .mapToObj(i -> serializer.serialize(array[i])) - .toList(); + .collect(Collectors.toList()); } @Override @@ -696,7 +698,7 @@ final class Serializers { @Override public List serialize(Object element) { final double[] array = (double[]) element; - return Arrays.stream(array).mapToObj(serializer::serialize).toList(); + return Arrays.stream(array).mapToObj(serializer::serialize).collect(Collectors.toList()); } @Override diff --git a/configlib-core/src/main/java/de/exlll/configlib/TypeSerializer.java b/configlib-core/src/main/java/de/exlll/configlib/TypeSerializer.java index 370be62..153be3b 100644 --- a/configlib-core/src/main/java/de/exlll/configlib/TypeSerializer.java +++ b/configlib-core/src/main/java/de/exlll/configlib/TypeSerializer.java @@ -2,11 +2,13 @@ package de.exlll.configlib; import de.exlll.configlib.ConfigurationElements.FieldElement; import de.exlll.configlib.ConfigurationElements.RecordComponentElement; +import de.exlll.configlib.util.PredicateUtil; +import de.exlll.configlib.util.ref.R$Class; +import de.exlll.configlib.util.ref.R$RecordComponent; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.lang.reflect.RecordComponent; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; @@ -17,9 +19,8 @@ import java.util.stream.Collectors; import static de.exlll.configlib.Validator.requireNonNull; -sealed abstract class TypeSerializer> - implements Serializer> - permits ConfigurationSerializer, RecordSerializer { +public abstract class TypeSerializer> + implements Serializer> { protected final Class type; protected final ConfigurationProperties properties; protected final NameFormatter formatter; @@ -39,13 +40,13 @@ sealed abstract class TypeSerializer> Class type, ConfigurationProperties properties ) { - return type.isRecord() + return R$Class.of(type).isRecord() ? new RecordSerializer<>(type, properties) : new ConfigurationSerializer<>(type, properties); } Map> buildSerializerMap() { - final var selector = new SerializerSelector(properties); + final SerializerSelector selector = new SerializerSelector(properties); try { return elements().stream().collect(Collectors.toMap( ConfigurationElement::name, @@ -79,20 +80,14 @@ sealed abstract class TypeSerializer> // This cast can lead to a ClassCastException if an element of type X is // serialized by a custom serializer that expects a different type Y. @SuppressWarnings("unchecked") - final var serializer = (Serializer) serializers.get(element.name()); + final Serializer serializer = (Serializer) serializers.get(element.name()); try { return (value != null) ? serializer.serialize(value) : null; } catch (ClassCastException e) { - String msg = ("Serialization of value '%s' for element '%s' of type '%s' failed.\n" + - "The type of the object to be serialized does not match the type " + - "the custom serializer of type '%s' expects.") - .formatted( - value, - element.element(), - element.declaringType(), - serializer.getClass() - ); - throw new ConfigurationException(msg, e); + throw new ConfigurationException( + "Serialization of value '" + value + "' for element '" + element.element() + "' of type '" + element.declaringType() + "' failed.\n" + + "The type of the object to be serialized does not match the type " + + "the custom serializer of type " + serializer.getClass() + "' expects.", e); } } @@ -100,17 +95,16 @@ sealed abstract class TypeSerializer> // This unchecked cast leads to an exception if the type of the object which // is deserialized is not a subtype of the type the deserializer expects. @SuppressWarnings("unchecked") - final var serializer = (Serializer) - serializers.get(element.name()); + final Serializer serializer = (Serializer) serializers.get(element.name()); final Object deserialized; try { deserialized = serializer.deserialize(value); } catch (ClassCastException e) { - String msg = baseDeserializeExceptionMessage(element, value) + "\n" + - "The type of the object to be deserialized does not " + - "match the type the deserializer expects."; - throw new ConfigurationException(msg, e); + throw new ConfigurationException( + baseDeserializeExceptionMessage(element, value) + "\n" + + "The type of the object to be deserialized does not " + + "match the type the deserializer expects.", e); } catch (RuntimeException e) { String msg = baseDeserializeExceptionMessage(element, value); throw new ConfigurationException(msg, e); @@ -121,12 +115,12 @@ sealed abstract class TypeSerializer> protected final Object[] deserializeConfigurationElements( Map serializedConfiguration ) { - final var elements = elements(); - final var result = new Object[elements.size()]; + final List elements = elements(); + final Object[] result = new Object[elements.size()]; for (int i = 0, size = elements.size(); i < size; i++) { - final var element = elements.get(i); - final var formattedName = formatter.format(element.name()); + final E element = elements.get(i); + final String formattedName = formatter.format(element.name()); if (!serializedConfiguration.containsKey(formattedName)) { final Object defaultValue = getDefaultValueOf(element); @@ -134,7 +128,7 @@ sealed abstract class TypeSerializer> continue; } - final var serializedValue = serializedConfiguration.get(formattedName); + final Object serializedValue = serializedConfiguration.get(formattedName); if ((serializedValue == null) && properties.inputNulls()) { // This statement (and hence the whole block) could be removed, @@ -159,12 +153,12 @@ sealed abstract class TypeSerializer> Object result = deserializeValue; boolean postProcessed = false; - for (final var entry : properties.getPostProcessorsByCondition().entrySet()) { - final var condition = entry.getKey(); + for (final Map.Entry>, UnaryOperator> entry : properties.getPostProcessorsByCondition().entrySet()) { + final Predicate> condition = entry.getKey(); if (!condition.test(element)) continue; - final var postProcessor = entry.getValue(); + final UnaryOperator postProcessor = entry.getValue(); result = tryApplyPostProcessorForElement(element, postProcessor, result); postProcessed = true; } @@ -186,14 +180,13 @@ sealed abstract class TypeSerializer> // This cast can lead to a ClassCastException if an element of type X is // annotated with a post-processor that takes values of some other type Y. @SuppressWarnings("unchecked") - final var pp = (UnaryOperator) postProcessor; + final UnaryOperator pp = (UnaryOperator) postProcessor; return pp.apply(value); } catch (ClassCastException e) { - String msg = ("Deserialization of value '%s' for element '%s' of type '%s' failed.\n" + - "The type of the object to be deserialized does not match the type " + - "post-processor '%s' expects.") - .formatted(value, element.element(), element.declaringType(), postProcessor); - throw new ConfigurationException(msg, e); + throw new ConfigurationException( + "Deserialization of value '" + value + "' for element '" + element.element() + "' of type '" + element.declaringType() + "' failed.\n" + + "The type of the object to be deserialized does not match the type " + + "post-processor '" + postProcessor + "' expects.", e); } } @@ -202,22 +195,22 @@ sealed abstract class TypeSerializer> ) { if (!element.type().isPrimitive()) return; - if (element instanceof RecordComponentElement recordComponentElement) { - final RecordComponent component = recordComponentElement.element(); - String msg = """ - Post-processors must not return null for primitive record \ - components but some post-processor of component '%s' of \ - record type '%s' does.\ - """.formatted(component, component.getDeclaringRecord()); - throw new ConfigurationException(msg); + if (element instanceof RecordComponentElement) { + RecordComponentElement recordComponentElement = (RecordComponentElement) element; + final R$RecordComponent component = recordComponentElement.element(); + throw new ConfigurationException( + "Post-processors must not return null for primitive record " + + "components but some post-processor of component '" + component + "' of " + + "record type '" + component.getDeclaringRecord() + "' does."); } - if (element instanceof FieldElement fieldElement) { + if (element instanceof FieldElement) { + FieldElement fieldElement = (FieldElement) element; final Field field = fieldElement.element(); - String msg = ("Post-processors must not return null for primitive fields " + - "but some post-processor of field '%s' does.") - .formatted(field); - throw new ConfigurationException(msg); + throw new ConfigurationException( + "Post-processors must not return null for primitive fields " + + "but some post-processor of field '" + field + "' does." + ); } throw new ConfigurationException("Unhandled ConfigurationElement: " + element); @@ -226,20 +219,22 @@ sealed abstract class TypeSerializer> private static void requireNonPrimitiveType(ConfigurationElement element) { if (!element.type().isPrimitive()) return; - if (element instanceof RecordComponentElement recordComponentElement) { - final RecordComponent component = recordComponentElement.element(); - String msg = ("Cannot set component '%s' of record type '%s' to null. " + - "Primitive types cannot be assigned null values.") - .formatted(component, component.getDeclaringRecord()); - throw new ConfigurationException(msg); + if (element instanceof RecordComponentElement) { + RecordComponentElement recordComponentElement = (RecordComponentElement) element; + final R$RecordComponent component = recordComponentElement.element(); + throw new ConfigurationException( + "Cannot set component '" + component + "' of record type '" + component.getDeclaringRecord() + "' to null. " + + "Primitive types cannot be assigned null values." + ); } - if (element instanceof FieldElement fieldElement) { + if (element instanceof FieldElement) { + FieldElement fieldElement = (FieldElement) element; final Field field = fieldElement.element(); - String msg = ("Cannot set field '%s' to null value. " + - "Primitive types cannot be assigned null.") - .formatted(field); - throw new ConfigurationException(msg); + throw new ConfigurationException( + "Cannot set field '" + field + "' to null value. " + + "Primitive types cannot be assigned null." + ); } throw new ConfigurationException("Unhandled ConfigurationElement: " + element); @@ -248,45 +243,45 @@ sealed abstract class TypeSerializer> final UnaryOperator createPostProcessorFromAnnotatedMethod() { final List list = Arrays.stream(type.getDeclaredMethods()) .filter(method -> method.isAnnotationPresent(PostProcess.class)) - .filter(Predicate.not(Method::isSynthetic)) - .filter(Predicate.not(this::isAccessorMethod)) - .toList(); + .filter(PredicateUtil.not(Method::isSynthetic)) + .filter(PredicateUtil.not(this::isAccessorMethod)) + .collect(Collectors.toList()); if (list.isEmpty()) return UnaryOperator.identity(); if (list.size() > 1) { - String methodNames = String.join("\n ", list.stream().map(Method::toString).toList()); - String msg = "Configuration types must not define more than one method for " + - "post-processing but type '%s' defines %d:\n %s" - .formatted(type, list.size(), methodNames); - throw new ConfigurationException(msg); + String methodNames = String.join("\n ", list.stream().map(Method::toString).collect(Collectors.toList())); + throw new ConfigurationException( + "Configuration types must not define more than one method for " + + "post-processing but type '" + type + "' defines " + list.size() + ":\n " + methodNames + ); } final Method method = list.get(0); final int modifiers = method.getModifiers(); if (Modifier.isAbstract(modifiers) || Modifier.isStatic(modifiers)) { - String msg = "Post-processing methods must be neither abstract nor static, " + - "but post-processing method '%s' of type '%s' is." - .formatted(method, type); - throw new ConfigurationException(msg); + throw new ConfigurationException( + "Post-processing methods must be neither abstract nor static, " + + "but post-processing method '" + method + "' of type '" + type + "' is." + ); } final int parameterCount = method.getParameterCount(); if (parameterCount > 0) { - String msg = "Post-processing methods must not define any parameters but " + - "post-processing method '%s' of type '%s' defines %d." - .formatted(method, type, parameterCount); - throw new ConfigurationException(msg); + throw new ConfigurationException( + "Post-processing methods must not define any parameters but " + + "post-processing method '" + method + "' of type '" + type + "' defines " + parameterCount + "." + ); } final Class returnType = method.getReturnType(); if ((returnType != void.class) && (returnType != type)) { - String msg = "The return type of post-processing methods must either be 'void' or " + - "the same type as the configuration type in which the post-processing " + - "method is defined. The return type of the post-processing method of " + - "type '%s' is neither 'void' nor '%s'." - .formatted(type, type.getSimpleName()); - throw new ConfigurationException(msg); + throw new ConfigurationException( + "The return type of post-processing methods must either be 'void' or " + + "the same type as the configuration type in which the post-processing " + + "method is defined. The return type of the post-processing method of " + + "type '" + type + "' is neither 'void' nor '" + type.getSimpleName() + "'." + ); } return object -> { @@ -303,11 +298,11 @@ sealed abstract class TypeSerializer> } final boolean isAccessorMethod(Method method) { - if (!type.isRecord()) return false; + if (!R$Class.of(type).isRecord()) return false; if (!method.getDeclaringClass().equals(type)) return false; if (method.getParameterCount() > 0) return false; - return Arrays.stream(type.getRecordComponents()) - .map(RecordComponent::getName) + return Arrays.stream(R$Class.of(type).getRecordComponents()) + .map(R$RecordComponent::getName) .anyMatch(s -> s.equals(method.getName())); } diff --git a/configlib-core/src/main/java/de/exlll/configlib/Validator.java b/configlib-core/src/main/java/de/exlll/configlib/Validator.java index c1d6955..7532b98 100644 --- a/configlib-core/src/main/java/de/exlll/configlib/Validator.java +++ b/configlib-core/src/main/java/de/exlll/configlib/Validator.java @@ -1,5 +1,7 @@ package de.exlll.configlib; +import de.exlll.configlib.util.ref.R$Class; + import java.util.Objects; final class Validator { @@ -29,7 +31,7 @@ final class Validator { static Class requireRecord(Class cls) { requireNonNull(cls, "type"); - if (!cls.isRecord()) { + if (!R$Class.of(cls).isRecord()) { String msg = "Class '" + cls.getSimpleName() + "' must be a record."; throw new ConfigurationException(msg); } diff --git a/configlib-core/src/main/java/de/exlll/configlib/util/MapUtil.java b/configlib-core/src/main/java/de/exlll/configlib/util/MapUtil.java new file mode 100644 index 0000000..d8b8e8d --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/util/MapUtil.java @@ -0,0 +1,14 @@ +package de.exlll.configlib.util; + +import java.util.AbstractMap; +import java.util.Map; + +public final class MapUtil { + private MapUtil() { + throw new UnsupportedOperationException("This class cannot be instantiated"); + } + + public static Map.Entry entry(K key, V value) { + return new AbstractMap.SimpleImmutableEntry<>(key, value); + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/util/PredicateUtil.java b/configlib-core/src/main/java/de/exlll/configlib/util/PredicateUtil.java new file mode 100644 index 0000000..8b46073 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/util/PredicateUtil.java @@ -0,0 +1,15 @@ +package de.exlll.configlib.util; + +import java.util.Objects; +import java.util.function.Predicate; + +public final class PredicateUtil { + private PredicateUtil() { + throw new UnsupportedOperationException("This class cannot be instantiated"); + } + + public static Predicate not(Predicate target) { + Objects.requireNonNull(target); + return (Predicate) target.negate(); + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/util/RefUtil.java b/configlib-core/src/main/java/de/exlll/configlib/util/RefUtil.java new file mode 100644 index 0000000..a2e70af --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/util/RefUtil.java @@ -0,0 +1,85 @@ +package de.exlll.configlib.util; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public final class RefUtil { + private RefUtil() { + throw new UnsupportedOperationException("This class cannot be instantiated"); + } + + private static final MethodHandles.Lookup lookup = MethodHandles.lookup(); + + public static MethodHandle bootstrapStaticMethod(String methodDesc) { + String[] descParts = splitMethodDesc(methodDesc); + Class owner; + try { + owner = Class.forName(descParts[0].replace('/', '.')); + } catch (ClassNotFoundException e) { + return null; + } + MethodType methodType; + try { + methodType = MethodType.fromMethodDescriptorString(descParts[2], owner.getClassLoader()); + } catch (TypeNotPresentException e) { + return null; + } + try { + return lookup.findStatic(owner, descParts[1], methodType); + } catch (NoSuchMethodException e) { + return null; + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static MethodHandle bootstrapVirtualMethod(String methodDesc) { + String[] descParts = splitMethodDesc(methodDesc); + Class owner; + try { + owner = Class.forName(descParts[0].replace('/', '.')); + } catch (ClassNotFoundException e) { + return null; + } + MethodType methodType; + try { + methodType = MethodType.fromMethodDescriptorString(descParts[2], owner.getClassLoader()); + } catch (TypeNotPresentException e) { + return null; + } + try { + return lookup.findVirtual(owner, descParts[1], methodType); + } catch (NoSuchMethodException e) { + return null; + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static String[] splitMethodDesc(String methodDesc) { + if (methodDesc.length() < 5) { // L;()V + throw new IllegalArgumentException("Invalid method descriptor: " + methodDesc); + } + + int descSplit = methodDesc.indexOf(';'); + if (descSplit == -1) { + throw new IllegalArgumentException("Invalid method descriptor: " + methodDesc); + } + + int argsStart = methodDesc.indexOf('('); + if (argsStart == -1) { + throw new IllegalArgumentException("Invalid method descriptor: " + methodDesc); + } + + return new String[] { + methodDesc.substring(1, descSplit), // java/lang/Object + methodDesc.substring(descSplit + 1, argsStart), // method name + methodDesc.substring(argsStart) // (Ljava/lang/Object;)V + }; + } + + public static RuntimeException sneakyThrow(Throwable t) throws T { + throw (T) t; + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/util/StringUtil.java b/configlib-core/src/main/java/de/exlll/configlib/util/StringUtil.java new file mode 100644 index 0000000..f1a6faa --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/util/StringUtil.java @@ -0,0 +1,64 @@ +package de.exlll.configlib.util; + +import java.util.Objects; + +public final class StringUtil { + private StringUtil() { + throw new UnsupportedOperationException("This class cannot be instantiated"); + } + + /** + * Returns a string consisting of a specific number of concatenated copies of an input string. For + * example, {@code repeat("hey", 3)} returns the string {@code "heyheyhey"}. + * + *

Java 11+ users: use {@code string.repeat(count)} instead. + * + * @param string any non-null string + * @param count the number of times to repeat it; a nonnegative integer + * @return a string containing {@code string} repeated {@code count} times (the empty string if + * {@code count} is zero) + * @throws IllegalArgumentException if {@code count} is negative + */ + public static String repeat(String string, int count) { + if (string == null) { + throw new IllegalArgumentException("string cannot be null"); + } + + if (count <= 1) { + if (count < 0) { + throw new IllegalArgumentException("invalid count: " + count); + } + return (count == 0) ? "" : string; + } + + // IF YOU MODIFY THE CODE HERE, you must update StringsRepeatBenchmark + final int len = string.length(); + final long longSize = (long) len * (long) count; + final int size = (int) longSize; + if (size != longSize) { + throw new ArrayIndexOutOfBoundsException("Required array size too large: " + longSize); + } + + final char[] array = new char[size]; + string.getChars(0, len, array, 0); + int n; + for (n = len; n < size - n; n <<= 1) { + System.arraycopy(array, 0, array, n, n); + } + System.arraycopy(array, 0, array, n, size - n); + return new String(array); + } + + public static boolean isBlank(String $this) { + final int strLen = $this.length(); + if (strLen == 0) { + return true; + } + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace($this.charAt(i))) { + return false; + } + } + return true; + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/util/ref/R$Class.java b/configlib-core/src/main/java/de/exlll/configlib/util/ref/R$Class.java new file mode 100644 index 0000000..6b59628 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/util/ref/R$Class.java @@ -0,0 +1,54 @@ +package de.exlll.configlib.util.ref; + +import de.exlll.configlib.util.RefUtil; + +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Array; + +public final class R$Class { + private static final MethodHandle v$isRecord = RefUtil.bootstrapVirtualMethod("Ljava/lang/Class;isRecord()Z"); + private static final MethodHandle v$getRecordComponents = RefUtil.bootstrapVirtualMethod("Ljava/lang/Class;getRecordComponents()[Ljava/lang/reflect/RecordComponent;"); + + private final Class instance; + + private R$Class(Class instance) { + this.instance = instance; + } + + public static R$Class of(Class instance) { + return new R$Class(instance); + } + + public Class instance() { + return instance; + } + + public boolean isRecord() { + if (v$isRecord == null) { + return false; + } + try { + return (boolean) v$isRecord.invoke(instance); + } catch (Throwable e) { + throw RefUtil.sneakyThrow(e); + } + } + + public R$RecordComponent[] getRecordComponents() { + if (v$getRecordComponents == null) { + throw new UnsupportedOperationException("Method java.lang.Class#getRecordComponents() not found"); + } + Object arr; + try { + arr = v$getRecordComponents.invoke(instance); + } catch (Throwable e) { + throw RefUtil.sneakyThrow(e); + } + int arrLength = Array.getLength(arr); + R$RecordComponent[] result = new R$RecordComponent[arrLength]; + for (int i = 0; i < arrLength; i++) { + result[i] = R$RecordComponent.of(Array.get(arr, i)); + } + return result; + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/util/ref/R$RecordComponent.java b/configlib-core/src/main/java/de/exlll/configlib/util/ref/R$RecordComponent.java new file mode 100644 index 0000000..650b9ae --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/util/ref/R$RecordComponent.java @@ -0,0 +1,122 @@ +package de.exlll.configlib.util.ref; + +import de.exlll.configlib.util.RefUtil; + +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandle; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Method; + +public final class R$RecordComponent implements AnnotatedElement { + + private static final MethodHandle v$getName = RefUtil.bootstrapVirtualMethod("Ljava/lang/reflect/RecordComponent;getName()Ljava/lang/String;"); + private static final MethodHandle v$getType = RefUtil.bootstrapVirtualMethod("Ljava/lang/reflect/RecordComponent;getType()Ljava/lang/Class;"); + private static final MethodHandle v$getAnnotatedType = RefUtil.bootstrapVirtualMethod("Ljava/lang/reflect/RecordComponent;getAnnotatedType()Ljava/lang/reflect/AnnotatedType;"); + private static final MethodHandle v$getDeclaringRecord = RefUtil.bootstrapVirtualMethod("Ljava/lang/reflect/RecordComponent;getDeclaringRecord()Ljava/lang/Class;"); + private static final MethodHandle v$getAccessor = RefUtil.bootstrapVirtualMethod("Ljava/lang/reflect/RecordComponent;getAccessor()Ljava/lang/reflect/Method;"); + + private final Object instance; + + private R$RecordComponent(Object instance) { + this.instance = instance; + } + + public Object instance() { + return instance; + } + + public static R$RecordComponent of(Object instance) { + return new R$RecordComponent(instance); + } + + public String getName() { + if (v$getName == null) { + throw new UnsupportedOperationException("Method java.lang.reflect.RecordComponent#getName() not found"); + } + try { + return (String) v$getName.invoke(instance); + } catch (Throwable e) { + throw RefUtil.sneakyThrow(e); + } + } + + public Class getType() { + if (v$getType == null) { + throw new UnsupportedOperationException("Method java.lang.reflect.RecordComponent#getType() not found"); + } + try { + return (Class) v$getType.invoke(instance); + } catch (Throwable e) { + throw RefUtil.sneakyThrow(e); + } + } + + public AnnotatedType getAnnotatedType() { + if (v$getAnnotatedType == null) { + throw new UnsupportedOperationException("Method java.lang.reflect.RecordComponent#getAnnotatedType() not found"); + } + try { + return (AnnotatedType) v$getAnnotatedType.invoke(instance); + } catch (Throwable e) { + throw RefUtil.sneakyThrow(e); + } + } + + public Class getDeclaringRecord() { + if (v$getDeclaringRecord == null) { + throw new UnsupportedOperationException("Method java.lang.reflect.RecordComponent#getDeclaringRecord() not found"); + } + try { + return (Class) v$getDeclaringRecord.invoke(instance); + } catch (Throwable e) { + throw RefUtil.sneakyThrow(e); + } + } + + public Method getAccessor() { + if (v$getAccessor == null) { + throw new UnsupportedOperationException("Method java.lang.reflect.RecordComponent#getAccessor() not found"); + } + try { + return (Method) v$getAccessor.invoke(instance); + } catch (Throwable e) { + throw RefUtil.sneakyThrow(e); + } + } + + // delegate AnnotatedElement + @Override + public T getAnnotation(Class annotationClass) { + return ((AnnotatedElement) instance).getAnnotation(annotationClass); + } + + @Override + public Annotation[] getAnnotations() { + return ((AnnotatedElement) instance).getAnnotations(); + } + + @Override + public Annotation[] getDeclaredAnnotations() { + return ((AnnotatedElement) instance).getDeclaredAnnotations(); + } + + @Override + public String toString() { + return instance.toString(); + } + + @Override + public final boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof R$RecordComponent)) return false; + + R$RecordComponent that = (R$RecordComponent) o; + return instance.equals(that.instance); + } + + @Override + public int hashCode() { + return instance.hashCode(); + } +} diff --git a/configlib-paper/build.gradle.kts b/configlib-paper/build.gradle.kts index b7e75b7..819e781 100644 --- a/configlib-paper/build.gradle.kts +++ b/configlib-paper/build.gradle.kts @@ -4,5 +4,5 @@ plugins { } dependencies { - compileOnly("io.papermc.paper:paper-api:1.20.2-R0.1-SNAPSHOT") + compileOnly("com.destroystokyo.paper:paper-api:1.16.5-R0.1-SNAPSHOT") } diff --git a/configlib-paper/src/main/java/de/exlll/configlib/ConfigLib.java b/configlib-paper/src/main/java/de/exlll/configlib/ConfigLib.java index 41d110e..05dcbcb 100644 --- a/configlib-paper/src/main/java/de/exlll/configlib/ConfigLib.java +++ b/configlib-paper/src/main/java/de/exlll/configlib/ConfigLib.java @@ -22,8 +22,8 @@ public final class ConfigLib extends JavaPlugin { return YamlConfigurationProperties .newBuilder() .addSerializerByCondition( - type -> type instanceof Class cls && - ConfigurationSerializable.class.isAssignableFrom(cls), + type -> type instanceof Class && + ConfigurationSerializable.class.isAssignableFrom((Class) type), BukkitConfigurationSerializableSerializer.DEFAULT ) .build(); diff --git a/configlib-velocity/build.gradle.kts b/configlib-velocity/build.gradle.kts index 1a19522..204793c 100644 --- a/configlib-velocity/build.gradle.kts +++ b/configlib-velocity/build.gradle.kts @@ -3,6 +3,11 @@ plugins { `plugins-config` } +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + dependencies { compileOnly("com.velocitypowered:velocity-api:3.2.0-SNAPSHOT") annotationProcessor("com.velocitypowered:velocity-api:3.2.0-SNAPSHOT") diff --git a/configlib-yaml/src/main/java/de/exlll/configlib/YamlConfigurationStore.java b/configlib-yaml/src/main/java/de/exlll/configlib/YamlConfigurationStore.java index e9f9ae4..27b830b 100644 --- a/configlib-yaml/src/main/java/de/exlll/configlib/YamlConfigurationStore.java +++ b/configlib-yaml/src/main/java/de/exlll/configlib/YamlConfigurationStore.java @@ -10,12 +10,14 @@ import org.snakeyaml.engine.v2.nodes.Node; import org.snakeyaml.engine.v2.nodes.Tag; import org.snakeyaml.engine.v2.representer.StandardRepresenter; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; +import java.util.Queue; import static de.exlll.configlib.Validator.requireNonNull; @@ -55,9 +57,9 @@ public final class YamlConfigurationStore implements public void write(T configuration, OutputStream outputStream) { requireNonNull(configuration, "configuration"); requireNonNull(outputStream, "output stream"); - var extractedCommentNodes = extractor.extractCommentNodes(configuration); - var yamlFileWriter = new YamlWriter(outputStream, properties); - var dumpedYaml = tryDump(configuration); + Queue extractedCommentNodes = extractor.extractCommentNodes(configuration); + YamlWriter yamlFileWriter = new YamlWriter(outputStream, properties); + String dumpedYaml = tryDump(configuration); yamlFileWriter.writeYaml(dumpedYaml, extractedCommentNodes); } @@ -66,9 +68,9 @@ public final class YamlConfigurationStore implements requireNonNull(configuration, "configuration"); requireNonNull(configurationFile, "configuration file"); tryCreateParentDirectories(configurationFile); - var extractedCommentNodes = extractor.extractCommentNodes(configuration); - var yamlFileWriter = new YamlWriter(configurationFile, properties); - var dumpedYaml = tryDump(configuration); + Queue extractedCommentNodes = extractor.extractCommentNodes(configuration); + YamlWriter yamlFileWriter = new YamlWriter(configurationFile, properties); + String dumpedYaml = tryDump(configuration); yamlFileWriter.writeYaml(dumpedYaml, extractedCommentNodes); } @@ -98,8 +100,8 @@ public final class YamlConfigurationStore implements public T read(InputStream inputStream) { requireNonNull(inputStream, "input stream"); try { - var yaml = YAML_LOADER.loadFromInputStream(inputStream); - var conf = requireYamlMapForRead(yaml); + Object yaml = YAML_LOADER.loadFromInputStream(inputStream); + Map conf = requireYamlMapForRead(yaml); return serializer.deserialize(conf); } catch (YamlEngineException e) { String msg = "The input stream does not contain valid YAML."; @@ -113,26 +115,25 @@ public final class YamlConfigurationStore implements throw new ConfigurationException(msg); } - if (!(yaml instanceof Map map)) { + if (!(yaml instanceof Map)) { String msg = "The contents of the input stream do not represent a configuration. " + "A valid configuration contains a YAML map but instead a " + "'" + yaml.getClass() + "' was found."; throw new ConfigurationException(msg); } - return map; + return (Map) yaml; } @Override public T load(Path configurationFile) { requireNonNull(configurationFile, "configuration file"); - try (var reader = Files.newBufferedReader(configurationFile, properties.getCharset())) { - var yaml = YAML_LOADER.loadFromReader(reader); - var conf = requireYamlMapForLoad(yaml, configurationFile); + try (BufferedReader reader = Files.newBufferedReader(configurationFile, properties.getCharset())) { + Object yaml = YAML_LOADER.loadFromReader(reader); + Map conf = requireYamlMapForLoad(yaml, configurationFile); return serializer.deserialize(conf); } catch (YamlEngineException e) { - String msg = "The configuration file at %s does not contain valid YAML."; - throw new ConfigurationException(msg.formatted(configurationFile), e); + throw new ConfigurationException("The configuration file at " + configurationFile + " does not contain valid YAML.", e); } catch (IOException e) { throw new RuntimeException(e); } @@ -140,18 +141,17 @@ public final class YamlConfigurationStore implements private Map requireYamlMapForLoad(Object yaml, Path configurationFile) { if (yaml == null) { - String msg = "The configuration file at %s is empty or only contains null."; - throw new ConfigurationException(msg.formatted(configurationFile)); + throw new ConfigurationException("The configuration file at " + configurationFile + " is empty or only contains null."); } - if (!(yaml instanceof Map map)) { - String msg = "The contents of the YAML file at %s do not represent a configuration. " + - "A valid configuration file contains a YAML map but instead a " + - "'" + yaml.getClass() + "' was found."; - throw new ConfigurationException(msg.formatted(configurationFile)); + if (!(yaml instanceof Map)) { + throw new ConfigurationException( + "The contents of the YAML file at " + configurationFile + " do not represent a configuration. " + + "A valid configuration file contains a YAML map but instead a " + + "'" + yaml.getClass() + "' was found."); } - return map; + return (Map) yaml; } @Override diff --git a/configlib-yaml/src/main/java/de/exlll/configlib/YamlConfigurations.java b/configlib-yaml/src/main/java/de/exlll/configlib/YamlConfigurations.java index 916f739..3667b22 100644 --- a/configlib-yaml/src/main/java/de/exlll/configlib/YamlConfigurations.java +++ b/configlib-yaml/src/main/java/de/exlll/configlib/YamlConfigurations.java @@ -27,7 +27,7 @@ public final class YamlConfigurations { * @see YamlConfigurationStore#load(Path) */ public static T load(Path configurationFile, Class configurationType) { - final var properties = YamlConfigurationProperties.newBuilder().build(); + final YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder().build(); return load(configurationFile, configurationType, properties); } @@ -52,7 +52,7 @@ public final class YamlConfigurations { Class configurationType, Consumer> propertiesConfigurer ) { - final var builder = YamlConfigurationProperties.newBuilder(); + final YamlConfigurationProperties.Builder builder = YamlConfigurationProperties.newBuilder(); propertiesConfigurer.accept(builder); return load(configurationFile, configurationType, builder.build()); } @@ -77,7 +77,7 @@ public final class YamlConfigurations { Class configurationType, YamlConfigurationProperties properties ) { - final var store = new YamlConfigurationStore<>(configurationType, properties); + final YamlConfigurationStore store = new YamlConfigurationStore<>(configurationType, properties); return store.load(configurationFile); } @@ -95,7 +95,7 @@ public final class YamlConfigurations { * @see YamlConfigurationStore#read(InputStream) */ public static T read(InputStream inputStream, Class configurationType) { - final var properties = YamlConfigurationProperties.newBuilder().build(); + final YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder().build(); return read(inputStream, configurationType, properties); } @@ -119,7 +119,7 @@ public final class YamlConfigurations { Class configurationType, Consumer> propertiesConfigurer ) { - final var builder = YamlConfigurationProperties.newBuilder(); + final YamlConfigurationProperties.Builder builder = YamlConfigurationProperties.newBuilder(); propertiesConfigurer.accept(builder); return read(inputStream, configurationType, builder.build()); } @@ -143,7 +143,7 @@ public final class YamlConfigurations { Class configurationType, YamlConfigurationProperties properties ) { - final var store = new YamlConfigurationStore<>(configurationType, properties); + final YamlConfigurationStore store = new YamlConfigurationStore<>(configurationType, properties); return store.read(inputStream); } @@ -163,7 +163,7 @@ public final class YamlConfigurations { * @see YamlConfigurationStore#update(Path) */ public static T update(Path configurationFile, Class configurationType) { - final var properties = YamlConfigurationProperties.newBuilder().build(); + final YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder().build(); return update(configurationFile, configurationType, properties); } @@ -189,7 +189,7 @@ public final class YamlConfigurations { Class configurationType, Consumer> propertiesConfigurer ) { - final var builder = YamlConfigurationProperties.newBuilder(); + final YamlConfigurationProperties.Builder builder = YamlConfigurationProperties.newBuilder(); propertiesConfigurer.accept(builder); return update(configurationFile, configurationType, builder.build()); } @@ -215,7 +215,7 @@ public final class YamlConfigurations { Class configurationType, YamlConfigurationProperties properties ) { - final var store = new YamlConfigurationStore<>(configurationType, properties); + final YamlConfigurationStore store = new YamlConfigurationStore<>(configurationType, properties); return store.update(configurationFile); } @@ -238,7 +238,7 @@ public final class YamlConfigurations { Class configurationType, T configuration ) { - final var properties = YamlConfigurationProperties.newBuilder().build(); + final YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder().build(); save(configurationFile, configurationType, configuration, properties); } @@ -264,7 +264,7 @@ public final class YamlConfigurations { T configuration, Consumer> propertiesConfigurer ) { - final var builder = YamlConfigurationProperties.newBuilder(); + final YamlConfigurationProperties.Builder builder = YamlConfigurationProperties.newBuilder(); propertiesConfigurer.accept(builder); save(configurationFile, configurationType, configuration, builder.build()); } @@ -290,7 +290,7 @@ public final class YamlConfigurations { T configuration, YamlConfigurationProperties properties ) { - final var store = new YamlConfigurationStore<>(configurationType, properties); + final YamlConfigurationStore store = new YamlConfigurationStore<>(configurationType, properties); store.save(configuration, configurationFile); } @@ -313,7 +313,7 @@ public final class YamlConfigurations { Class configurationType, T configuration ) { - final var properties = YamlConfigurationProperties.newBuilder().build(); + final YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder().build(); write(outputStream, configurationType, configuration, properties); } @@ -339,7 +339,7 @@ public final class YamlConfigurations { T configuration, Consumer> propertiesConfigurer ) { - final var builder = YamlConfigurationProperties.newBuilder(); + final YamlConfigurationProperties.Builder builder = YamlConfigurationProperties.newBuilder(); propertiesConfigurer.accept(builder); write(outputStream, configurationType, configuration, builder.build()); } @@ -365,7 +365,7 @@ public final class YamlConfigurations { T configuration, YamlConfigurationProperties properties ) { - final var store = new YamlConfigurationStore<>(configurationType, properties); + final YamlConfigurationStore store = new YamlConfigurationStore<>(configurationType, properties); store.write(configuration, outputStream); } } diff --git a/configlib-yaml/src/main/java/de/exlll/configlib/YamlWriter.java b/configlib-yaml/src/main/java/de/exlll/configlib/YamlWriter.java index b74fe72..34f31ae 100644 --- a/configlib-yaml/src/main/java/de/exlll/configlib/YamlWriter.java +++ b/configlib-yaml/src/main/java/de/exlll/configlib/YamlWriter.java @@ -107,8 +107,8 @@ final class YamlWriter { * of a child. That order ultimately represents the order in which the * YAML file is structured. */ - var node = nodes.poll(); - var currentIndentLevel = 0; + CommentNode node = nodes.poll(); + int currentIndentLevel = 0; for (final String line : yaml.split("\n")) { if (node == null) { @@ -116,16 +116,16 @@ final class YamlWriter { continue; } - final var elementNames = node.elementNames(); - final var indent = " ".repeat(currentIndentLevel); + final List elementNames = node.elementNames(); + final String indent = " ".repeat(currentIndentLevel); - final var lineStart = indent + elementNames.get(currentIndentLevel) + ":"; + final String lineStart = indent + elementNames.get(currentIndentLevel) + ":"; if (!line.startsWith(lineStart)) { writeLine(line); continue; } - final var commentIndentLevel = elementNames.size() - 1; + final int commentIndentLevel = elementNames.size() - 1; if (currentIndentLevel++ == commentIndentLevel) { writeComments(node.comments(), commentIndentLevel); if ((node = nodes.poll()) != null) {