diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java index 4ed61dbf29..40021e5f39 100644 --- a/api/src/main/java/module-info.java +++ b/api/src/main/java/module-info.java @@ -9,8 +9,8 @@ @NullMarked module net.kyori.adventure.api { requires transitive net.kyori.adventure.key; - requires transitive org.jspecify; - requires transitive org.jetbrains.annotations; + requires static transitive org.jspecify; + requires static transitive org.jetbrains.annotations; exports net.kyori.adventure; exports net.kyori.adventure.audience; diff --git a/serializer-configurate4/src/main/java/module-info.java b/serializer-configurate4/src/main/java/module-info.java index b0fb65c134..90c3cbf6f3 100644 --- a/serializer-configurate4/src/main/java/module-info.java +++ b/serializer-configurate4/src/main/java/module-info.java @@ -7,9 +7,9 @@ module net.kyori.adventure.serializer.configurate4 { requires transitive net.kyori.adventure.api; requires net.kyori.adventure.text.serializer.commons; - requires io.leangen.geantyref; - requires org.spongepowered.configurate; - requires static org.checkerframework.checker.qual; + requires transitive io.leangen.geantyref; + requires transitive org.spongepowered.configurate; + requires static transitive org.checkerframework.checker.qual; exports net.kyori.adventure.serializer.configurate4; } diff --git a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ClickEventPayloadSerializer.java b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ClickEventPayloadSerializer.java new file mode 100644 index 0000000000..cb8f7ad8df --- /dev/null +++ b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ClickEventPayloadSerializer.java @@ -0,0 +1,65 @@ +/* + * This file is part of adventure, licensed under the MIT License. + * + * Copyright (c) 2017-2025 KyoriPowered + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package net.kyori.adventure.serializer.configurate4; + +import io.leangen.geantyref.GenericTypeReflector; +import java.lang.reflect.Type; +import net.kyori.adventure.text.event.ClickEvent; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.serialize.SerializationException; +import org.spongepowered.configurate.serialize.TypeSerializer; + +final class ClickEventPayloadSerializer implements TypeSerializer { + static final TypeSerializer INSTANCE = new ClickEventPayloadSerializer(); + + private ClickEventPayloadSerializer() { + } + + @Override + public ClickEvent.Payload deserialize(final Type type, final ConfigurationNode node) throws SerializationException { + final Class raw = GenericTypeReflector.erase(type).asSubclass(ClickEvent.Payload.class); + if (ClickEvent.Payload.Custom.class.equals(raw) || ClickEvent.Payload.Dialog.class.equals(raw)) { + throw new SerializationException("Do not know how to deserialize a " + raw.getSimpleName() + " payload"); + } else if (ClickEvent.Payload.Int.class.equals(raw)) { + return ClickEvent.Payload.integer(node.getInt()); + } else if (ClickEvent.Payload.Text.class.equals(raw)) { + return ClickEvent.Payload.string(node.getString()); + } else { + throw new SerializationException("Do not know how to deserialize a " + raw.getSimpleName() + " payload!"); + } + } + + @Override + public void serialize(final Type type, final ClickEvent.@Nullable Payload obj, final ConfigurationNode node) throws SerializationException { + switch (obj) { + case null -> node.set(null); + case ClickEvent.Payload.Custom c -> throw new SerializationException("Do not know how to serialize a custom payload"); + case ClickEvent.Payload.Dialog d -> throw new SerializationException("Do not know how to serialize a dialog"); + case ClickEvent.Payload.Int i -> node.set(i.integer()); + case ClickEvent.Payload.Text s -> node.set(s.value()); + } + + } +} diff --git a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ComponentTypeSerializer.java b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ComponentTypeSerializer.java index a0cb77d80b..1ef192cce4 100644 --- a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ComponentTypeSerializer.java +++ b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ComponentTypeSerializer.java @@ -97,17 +97,13 @@ public Component deserialize(final Type type, final ConfigurationNode value) thr return this.deserialize0(value); } - private BuildableComponent deserialize0(final ConfigurationNode value) throws SerializationException { + private Component deserialize0(final ConfigurationNode value) throws SerializationException { // Try to read as a string if (!value.isList() && !value.isMap()) { final String str = value.getString(); if (str != null) { if (this.stringSerial != null) { - final Component ret = this.stringSerial.deserialize(str); - if (!(ret instanceof BuildableComponent)) { - throw new SerializationException("Result " + ret + " is not builable"); - } - return (BuildableComponent) ret; + return this.stringSerial.deserialize(str); } else { return Component.text(str); } @@ -115,7 +111,7 @@ public Component deserialize(final Type type, final ConfigurationNode value) thr } else if (value.isList()) { ComponentBuilder parent = null; for (final ConfigurationNode childElement : value.childrenList()) { - final BuildableComponent child = this.deserialize0(childElement); + final Component child = this.deserialize0(childElement); if (parent == null) { parent = child.toBuilder(); } else { @@ -278,13 +274,13 @@ public void serialize(final Type type, final @Nullable Component src, final Conf score.node(SCORE_OBJECTIVE).set(sc.objective()); // score component value is optional @SuppressWarnings("deprecation") - final @Nullable String scoreValue = sc.value(); + final String scoreValue = sc.value(); if (scoreValue != null) score.node(SCORE_VALUE).set(scoreValue); } else if (src instanceof SelectorComponent) { value.node(SELECTOR).set(((SelectorComponent) src).pattern()); } else if (src instanceof KeybindComponent) { value.node(KEYBIND).set(((KeybindComponent) src).keybind()); - } else if (src instanceof final NBTComponent nc) { + } else if (src instanceof final NBTComponent nc) { value.node(NBT).set(nc.nbtPath()); value.node(NBT_INTERPRET).set(nc.interpret()); switch (src) { @@ -293,17 +289,14 @@ public void serialize(final Type type, final @Nullable Component src, final Conf case StorageNBTComponent storageNBTComponent -> value.node(NBT_STORAGE).set(KeySerializer.INSTANCE.type(), storageNBTComponent.storage()); default -> throw notSureHowToSerialize(src); } - } else if (src instanceof ObjectComponent) { - final ObjectComponent objectComponent = (ObjectComponent) src; + } else if (src instanceof final ObjectComponent objectComponent) { final ObjectContents contents = objectComponent.contents(); - if (contents instanceof SpriteObjectContents) { - final SpriteObjectContents spriteContents = (SpriteObjectContents) contents; + if (contents instanceof final SpriteObjectContents spriteContents) { if (!spriteContents.atlas().equals(SpriteObjectContents.DEFAULT_ATLAS)) { value.node(OBJECT_ATLAS).set(KeySerializer.INSTANCE.type(), spriteContents.atlas()); } value.node(OBJECT_SPRITE).set(KeySerializer.INSTANCE.type(), spriteContents.sprite()); - } else if (contents instanceof PlayerHeadObjectContents) { - final PlayerHeadObjectContents playerHeadContents = (PlayerHeadObjectContents) contents; + } else if (contents instanceof final PlayerHeadObjectContents playerHeadContents) { value.node(OBJECT_HAT).set(playerHeadContents.hat()); final String playerName = playerHeadContents.name(); final UUID playerId = playerHeadContents.id(); @@ -348,7 +341,7 @@ public void serialize(final Type type, final @Nullable Component src, final Conf } } - private static , B extends NBTComponentBuilder> B nbt(final B builder, final String nbt, final boolean interpret) { + private static , B extends NBTComponentBuilder> B nbt(final B builder, final String nbt, final boolean interpret) { return builder .nbtPath(nbt) .interpret(interpret); diff --git a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ConfigurateComponentSerializerImpl.java b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ConfigurateComponentSerializerImpl.java index 83fb37d779..871a69c020 100644 --- a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ConfigurateComponentSerializerImpl.java +++ b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ConfigurateComponentSerializerImpl.java @@ -29,8 +29,8 @@ import net.kyori.adventure.sound.SoundStop; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TranslationArgument; -import net.kyori.adventure.text.event.ClickEventImpl; -import net.kyori.adventure.text.event.HoverEventImpl; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.ShadowColor; import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextDecoration; @@ -66,7 +66,7 @@ private ConfigurateComponentSerializerImpl(final Builder builder) { @Override public Component deserialize(final ConfigurationNode input) { try { - final @Nullable Component deserialized = input.get(Component.class); + final Component deserialized = input.get(Component.class); if (deserialized != null) { return deserialized; } @@ -102,12 +102,13 @@ private TypeSerializerCollection makeSerializers(final TypeSerializerCollection. .register(TextColorSerializer.INSTANCE) .register(BlockNBTPosSerializer.INSTANCE) .register(TranslationArgument.class, TranslationArgumentTypeSerializer.INSTANCE) - .registerExact(new IndexSerializer<>(TypeToken.get(ClickEventImpl.Action.class), ClickEventImpl.Action.NAMES)) - .registerExact(new IndexSerializer<>(new TypeToken>() {}, HoverEventImpl.Action.NAMES)) + .register(ClickEvent.Payload.class, ClickEventPayloadSerializer.INSTANCE) + .registerExact(new IndexSerializer<>(new TypeToken>() {}, ClickEvent.Action.NAMES)) + .registerExact(new IndexSerializer<>(new TypeToken>() {}, HoverEvent.Action.NAMES)) .registerExact(new IndexSerializer<>(TypeToken.get(Sound.Source.class), Sound.Source.NAMES)) .registerExact(new IndexSerializer<>(TypeToken.get(TextDecoration.class), TextDecoration.NAMES)) - .registerExact(HoverEventImpl.ShowEntity.class, HoverEventShowEntitySerializer.INSTANCE) - .registerExact(HoverEventImpl.ShowItem.class, HoverEventShowItemSerializer.INSTANCE) + .registerExact(HoverEvent.ShowEntity.class, HoverEventShowEntitySerializer.INSTANCE) + .registerExact(HoverEvent.ShowItem.class, HoverEventShowItemSerializer.INSTANCE) .register(ConfigurateDataComponentValue.class, ConfigurateDataComponentValueTypeSerializer.INSTANCE) .register(ShadowColor.class, ShadowColorSerializer.INSTACE) .register(PlayerHeadObjectContents.ProfileProperty.class, ProfilePropertySerializer.INSTANCE) diff --git a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ConfigurateDataComponentValue.java b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ConfigurateDataComponentValue.java index c2e0b72071..262629f3df 100644 --- a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ConfigurateDataComponentValue.java +++ b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ConfigurateDataComponentValue.java @@ -31,7 +31,7 @@ * * @since 4.17.0 */ -public interface ConfigurateDataComponentValue extends DataComponentValue { +public sealed interface ConfigurateDataComponentValue extends DataComponentValue permits SnapshottingConfigurateDataComponentValue { /** * Create a data component value capturing the value of an existing node. * diff --git a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/HoverEventShowEntitySerializer.java b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/HoverEventShowEntitySerializer.java index 0cf7f26b26..4ca093e132 100644 --- a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/HoverEventShowEntitySerializer.java +++ b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/HoverEventShowEntitySerializer.java @@ -27,33 +27,33 @@ import java.util.UUID; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.HoverEventImpl; +import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.serializer.commons.ComponentTreeConstants; import org.jspecify.annotations.Nullable; import org.spongepowered.configurate.ConfigurationNode; import org.spongepowered.configurate.serialize.SerializationException; import org.spongepowered.configurate.serialize.TypeSerializer; -final class HoverEventShowEntitySerializer implements TypeSerializer { +final class HoverEventShowEntitySerializer implements TypeSerializer { static final HoverEventShowEntitySerializer INSTANCE = new HoverEventShowEntitySerializer(); private HoverEventShowEntitySerializer() { } @Override - public HoverEventImpl.ShowEntity deserialize(final Type type, final ConfigurationNode value) throws SerializationException { + public HoverEvent.ShowEntity deserialize(final Type type, final ConfigurationNode value) throws SerializationException { final Key typeId = value.node(ComponentTreeConstants.SHOW_ENTITY_TYPE).get(Key.class); final UUID id = value.node(ComponentTreeConstants.SHOW_ENTITY_ID).get(UUID.class); if (typeId == null || id == null) { throw new SerializationException("A show entity hover event needs type and id fields to be deserialized"); } - final @Nullable Component name = value.node(ComponentTreeConstants.SHOW_ENTITY_NAME).get(Component.class); + final Component name = value.node(ComponentTreeConstants.SHOW_ENTITY_NAME).get(Component.class); - return HoverEventImpl.ShowEntity.showEntity(typeId, id, name); + return HoverEvent.ShowEntity.showEntity(typeId, id, name); } @Override - public void serialize(final Type type, final HoverEventImpl.@Nullable ShowEntity obj, final ConfigurationNode value) throws SerializationException { + public void serialize(final Type type, final HoverEvent.@Nullable ShowEntity obj, final ConfigurationNode value) throws SerializationException { if (obj == null) { value.set(null); return; diff --git a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/HoverEventShowItemSerializer.java b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/HoverEventShowItemSerializer.java index 755bb921a2..b9f82bc79e 100644 --- a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/HoverEventShowItemSerializer.java +++ b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/HoverEventShowItemSerializer.java @@ -29,14 +29,14 @@ import java.util.Map; import net.kyori.adventure.key.Key; import net.kyori.adventure.nbt.api.BinaryTagHolder; -import net.kyori.adventure.text.event.HoverEventImpl; +import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.serializer.commons.ComponentTreeConstants; import org.jspecify.annotations.Nullable; import org.spongepowered.configurate.ConfigurationNode; import org.spongepowered.configurate.serialize.SerializationException; import org.spongepowered.configurate.serialize.TypeSerializer; -final class HoverEventShowItemSerializer implements TypeSerializer { +final class HoverEventShowItemSerializer implements TypeSerializer { static final HoverEventShowItemSerializer INSTANCE = new HoverEventShowItemSerializer(); private static final TypeToken> COMPONENT_MAP_TYPE = new TypeToken>() { @@ -46,7 +46,7 @@ private HoverEventShowItemSerializer() { } @Override - public HoverEventImpl.ShowItem deserialize(final Type type, final ConfigurationNode value) throws SerializationException { + public HoverEvent.ShowItem deserialize(final Type type, final ConfigurationNode value) throws SerializationException { final Key id = value.node(ComponentTreeConstants.SHOW_ITEM_ID).get(Key.class); if (id == null) { throw new SerializationException("An id is required to deserialize the show_item hover event"); @@ -56,17 +56,17 @@ public HoverEventImpl.ShowItem deserialize(final Type type, final ConfigurationN if (!components.virtual()) { final Map componentsMap = components.require(COMPONENT_MAP_TYPE); - return HoverEventImpl.ShowItem.showItem(id, count, new HashMap<>(componentsMap)); + return HoverEvent.ShowItem.showItem(id, count, new HashMap<>(componentsMap)); } else { // legacy (pre-1.20.5) @SuppressWarnings("deprecation") final String tag = value.node(ComponentTreeConstants.SHOW_ITEM_TAG).getString(); - return HoverEventImpl.ShowItem.showItem(id, count, tag == null ? null : BinaryTagHolder.binaryTagHolder(tag)); + return HoverEvent.ShowItem.showItem(id, count, tag == null ? null : BinaryTagHolder.binaryTagHolder(tag)); } } @Override - public void serialize(final Type type, final HoverEventImpl.@Nullable ShowItem obj, final ConfigurationNode value) throws SerializationException { + public void serialize(final Type type, final HoverEvent.@Nullable ShowItem obj, final ConfigurationNode value) throws SerializationException { if (obj == null) { value.set(null); return; diff --git a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ShadowColorSerializer.java b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ShadowColorSerializer.java index 4093de4ac3..6169153d02 100644 --- a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ShadowColorSerializer.java +++ b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/ShadowColorSerializer.java @@ -35,7 +35,7 @@ final class ShadowColorSerializer implements TypeSerializer { static final TypeSerializer INSTACE = new ShadowColorSerializer(false); - private boolean emitFloats; + private final boolean emitFloats; private ShadowColorSerializer(final boolean emitFloats) { this.emitFloats = emitFloats; diff --git a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/SnapshottingConfigurateDataComponentValue.java b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/SnapshottingConfigurateDataComponentValue.java index dafd398a99..f5a85c98d1 100644 --- a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/SnapshottingConfigurateDataComponentValue.java +++ b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/SnapshottingConfigurateDataComponentValue.java @@ -23,16 +23,12 @@ */ package net.kyori.adventure.serializer.configurate4; -import java.util.stream.Stream; -import net.kyori.examination.ExaminableProperty; import org.spongepowered.configurate.AttributedConfigurationNode; import org.spongepowered.configurate.BasicConfigurationNode; import org.spongepowered.configurate.CommentedConfigurationNode; import org.spongepowered.configurate.ConfigurationNode; -final class SnapshottingConfigurateDataComponentValue implements ConfigurateDataComponentValue { - private final ConfigurationNode ownedNode; - +record SnapshottingConfigurateDataComponentValue(ConfigurationNode ownedNode) implements ConfigurateDataComponentValue { // capture the value of an existing node without exposing any mutable state static SnapshottingConfigurateDataComponentValue create(final ConfigurationNode existing) { final ConfigurationNode owned; @@ -49,19 +45,8 @@ static SnapshottingConfigurateDataComponentValue create(final ConfigurationNode return new SnapshottingConfigurateDataComponentValue(owned); } - private SnapshottingConfigurateDataComponentValue(final ConfigurationNode owned) { - this.ownedNode = owned; - } - @Override public void applyTo(final ConfigurationNode node) { node.from(this.ownedNode); } - - @Override - public Stream examinableProperties() { - return Stream.of( - ExaminableProperty.of("ownedNode", this.ownedNode) - ); - } } diff --git a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/StyleSerializer.java b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/StyleSerializer.java index 51caf86f9c..c40554503d 100644 --- a/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/StyleSerializer.java +++ b/serializer-configurate4/src/main/java/net/kyori/adventure/serializer/configurate4/StyleSerializer.java @@ -28,9 +28,7 @@ import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.event.ClickEventImpl; import net.kyori.adventure.text.event.HoverEvent; -import net.kyori.adventure.text.event.HoverEventImpl; import net.kyori.adventure.text.format.ShadowColor; import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextColor; @@ -58,7 +56,7 @@ final class StyleSerializer implements TypeSerializer