diff --git a/.checkstyle/suppressions.xml b/.checkstyle/suppressions.xml index ea0de2320..7fd987d8e 100644 --- a/.checkstyle/suppressions.xml +++ b/.checkstyle/suppressions.xml @@ -5,7 +5,7 @@ - + diff --git a/api/src/main/java/net/kyori/adventure/util/Services.java b/api/src/main/java/net/kyori/adventure/util/Services.java index 038e47fbf..b1ca003be 100644 --- a/api/src/main/java/net/kyori/adventure/util/Services.java +++ b/api/src/main/java/net/kyori/adventure/util/Services.java @@ -69,4 +69,56 @@ private Services() { } return Optional.empty(); } + + /** + * A fallback service. + * + *

When used in tandem with {@link #serviceWithFallback(Class)}, classes that implement this interface + * will be ignored in favour of classes that do not implement this interface.

+ * + * @since 4.14.0 + */ + public interface Fallback { + } + + /** + * Locates a service. + * + *

If multiple services of this type exist, the first non-fallback service will be returned.

+ * + * @param type the service type + * @param

the service type + * @return a service, or {@link Optional#empty()} + * @see Fallback + * @since 4.14.0 + */ + public static

@NotNull Optional

serviceWithFallback(final @NotNull Class

type) { + final ServiceLoader

loader = Services0.loader(type); + final Iterator

it = loader.iterator(); + P firstFallback = null; + + while (it.hasNext()) { + final P instance; + + try { + instance = it.next(); + } catch (final Throwable t) { + if (SERVICE_LOAD_FAILURES_ARE_FATAL) { + throw new IllegalStateException("Encountered an exception loading service " + type, t); + } else { + continue; + } + } + + if (instance instanceof Fallback) { + if (firstFallback == null) { + firstFallback = instance; + } + } else { + return Optional.of(instance); + } + } + + return Optional.ofNullable(firstFallback); + } } diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts index 38362c64f..758e0d5b3 100644 --- a/bom/build.gradle.kts +++ b/bom/build.gradle.kts @@ -23,7 +23,8 @@ dependencies { "text-minimessage", "text-serializer-ansi", "text-serializer-gson", - "text-serializer-gson-legacy-impl", + "text-serializer-json", + "text-serializer-json-legacy-impl", "text-serializer-legacy", "text-serializer-plain" ).forEach { diff --git a/build-logic/src/main/kotlin/adventure.common-conventions.gradle.kts b/build-logic/src/main/kotlin/adventure.common-conventions.gradle.kts index 631b7dbe9..76ccc27f0 100644 --- a/build-logic/src/main/kotlin/adventure.common-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/adventure.common-conventions.gradle.kts @@ -3,7 +3,6 @@ import com.diffplug.gradle.spotless.FormatExtension import me.champeau.jmh.JMHPlugin import me.champeau.jmh.JmhParameters import net.ltgt.gradle.errorprone.errorprone -import org.gradle.api.artifacts.type.ArtifactTypeDefinition plugins { id("adventure.base-conventions") diff --git a/build-logic/src/main/kotlin/adventure.json-impl-conventions.gradle.kts b/build-logic/src/main/kotlin/adventure.json-impl-conventions.gradle.kts new file mode 100644 index 000000000..5cf02bde3 --- /dev/null +++ b/build-logic/src/main/kotlin/adventure.json-impl-conventions.gradle.kts @@ -0,0 +1,66 @@ +import me.champeau.jmh.JMHPlugin +import me.champeau.jmh.JmhBytecodeGeneratorTask + +plugins { + id("adventure.common-conventions") +} + +val sharedTests by configurations.registering { + isVisible = false + isCanBeResolved = false + isCanBeConsumed = false +} + +val sharedTestDirs by configurations.registering { + isVisible = false + isCanBeConsumed = false + extendsFrom(sharedTests.get()) + isTransitive = false // we want the directory on its own + + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY)) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_API)) // needs to be API to get the unpacked test-fixtures variant + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.CLASSES)) + } +} + +val sharedBenchmarks by configurations.registering { + isVisible = false + isTransitive = false + + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY)) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.CLASSES)) + } +} + +configurations.testRuntimeOnly { + extendsFrom(sharedTests.get()) +} + +tasks.test { + testClassesDirs += sharedTestDirs.get() +} + +dependencies { + val textSerializerJson = project(":adventure-text-serializer-json") + api(textSerializerJson) + sharedTests.name(testFixtures(textSerializerJson.copy())) + sharedBenchmarks.name(textSerializerJson.copy().capabilities { + requireCapability("${project.group}:adventure-text-serializer-json-benchmarks:${project.version}") + }) + annotationProcessor(project(":adventure-annotation-processors")) +} + +// Configure benchmarks to read from json project +plugins.withId("me.champeau.jmh") { + configurations.named(JMHPlugin.getJHM_RUNTIME_CLASSPATH_CONFIGURATION()) { + extendsFrom(sharedBenchmarks.get()) + } + tasks.named("jmhRunBytecodeGenerator", JmhBytecodeGeneratorTask::class) { + classesDirsToProcess.from(sharedBenchmarks.get()) + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 6e8875da9..031b4b2b3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -43,6 +43,8 @@ sequenceOf( "text-minimessage", "text-serializer-gson", "text-serializer-gson-legacy-impl", + "text-serializer-json", + "text-serializer-json-legacy-impl", "text-serializer-legacy", "text-serializer-plain", "text-serializer-ansi", diff --git a/text-serializer-gson-legacy-impl/build.gradle.kts b/text-serializer-gson-legacy-impl/build.gradle.kts index ac5adbc5e..7718b6f04 100644 --- a/text-serializer-gson-legacy-impl/build.gradle.kts +++ b/text-serializer-gson-legacy-impl/build.gradle.kts @@ -3,9 +3,8 @@ plugins { } dependencies { - api(projects.adventureApi) api(projects.adventureTextSerializerGson) - api(projects.adventureNbt) + api(projects.adventureTextSerializerJsonLegacyImpl) } applyJarMetadata("net.kyori.adventure.text.serializer.gson.legacyimpl") diff --git a/text-serializer-gson-legacy-impl/src/main/java/net/kyori/adventure/text/serializer/gson/legacyimpl/NBTLegacyHoverEventSerializer.java b/text-serializer-gson-legacy-impl/src/main/java/net/kyori/adventure/text/serializer/gson/legacyimpl/NBTLegacyHoverEventSerializer.java index 6065cbd6e..a9811f6e0 100644 --- a/text-serializer-gson-legacy-impl/src/main/java/net/kyori/adventure/text/serializer/gson/legacyimpl/NBTLegacyHoverEventSerializer.java +++ b/text-serializer-gson-legacy-impl/src/main/java/net/kyori/adventure/text/serializer/gson/legacyimpl/NBTLegacyHoverEventSerializer.java @@ -25,20 +25,27 @@ import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.serializer.gson.LegacyHoverEventSerializer; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; /** * A legacy {@link HoverEvent} serializer. * * @since 4.3.0 + * @deprecated for removal since 4.14, use {@link net.kyori.adventure.text.serializer.json.legacyimpl.NBTLegacyHoverEventSerializer the text-serializer-json version} instead. */ -public interface NBTLegacyHoverEventSerializer extends LegacyHoverEventSerializer { +@Deprecated +@ApiStatus.ScheduledForRemoval(inVersion = "5.0.0") +public interface NBTLegacyHoverEventSerializer extends LegacyHoverEventSerializer, net.kyori.adventure.text.serializer.json.legacyimpl.NBTLegacyHoverEventSerializer { /** * Gets the legacy {@link HoverEvent} serializer. * * @return a legacy {@link HoverEvent} serializer * @since 4.3.0 + * @deprecated for removal since 4.14, use {@link net.kyori.adventure.text.serializer.json.legacyimpl.NBTLegacyHoverEventSerializer the text-serializer-json version} instead. */ + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "5.0.0") static @NotNull LegacyHoverEventSerializer get() { return NBTLegacyHoverEventSerializerImpl.INSTANCE; } diff --git a/text-serializer-gson-legacy-impl/src/main/java/net/kyori/adventure/text/serializer/gson/legacyimpl/NBTLegacyHoverEventSerializerImpl.java b/text-serializer-gson-legacy-impl/src/main/java/net/kyori/adventure/text/serializer/gson/legacyimpl/NBTLegacyHoverEventSerializerImpl.java index 4603d0ee0..d60ff7780 100644 --- a/text-serializer-gson-legacy-impl/src/main/java/net/kyori/adventure/text/serializer/gson/legacyimpl/NBTLegacyHoverEventSerializerImpl.java +++ b/text-serializer-gson-legacy-impl/src/main/java/net/kyori/adventure/text/serializer/gson/legacyimpl/NBTLegacyHoverEventSerializerImpl.java @@ -24,85 +24,39 @@ package net.kyori.adventure.text.serializer.gson.legacyimpl; import java.io.IOException; -import java.util.UUID; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.nbt.CompoundBinaryTag; -import net.kyori.adventure.nbt.TagStringIO; -import net.kyori.adventure.nbt.api.BinaryTagHolder; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.serializer.gson.LegacyHoverEventSerializer; +import net.kyori.adventure.text.serializer.json.legacyimpl.NBTLegacyHoverEventSerializer; import net.kyori.adventure.util.Codec; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +@Deprecated final class NBTLegacyHoverEventSerializerImpl implements LegacyHoverEventSerializer { static final NBTLegacyHoverEventSerializerImpl INSTANCE = new NBTLegacyHoverEventSerializerImpl(); - private static final TagStringIO SNBT_IO = TagStringIO.get(); - private static final Codec SNBT_CODEC = Codec.codec(SNBT_IO::asCompound, SNBT_IO::asString); - static final String ITEM_TYPE = "id"; - static final String ITEM_COUNT = "Count"; - static final String ITEM_TAG = "tag"; - - static final String ENTITY_NAME = "name"; - static final String ENTITY_TYPE = "type"; - static final String ENTITY_ID = "id"; + static final net.kyori.adventure.text.serializer.json.LegacyHoverEventSerializer NEW_INSTANCE = NBTLegacyHoverEventSerializer.get(); private NBTLegacyHoverEventSerializerImpl() { } @Override public HoverEvent.@NotNull ShowItem deserializeShowItem(final @NotNull Component input) throws IOException { - assertTextComponent(input); - final CompoundBinaryTag contents = SNBT_CODEC.decode(((TextComponent) input).content()); - final CompoundBinaryTag tag = contents.getCompound(ITEM_TAG); - return HoverEvent.ShowItem.showItem( - Key.key(contents.getString(ITEM_TYPE)), - contents.getByte(ITEM_COUNT, (byte) 1), - tag == CompoundBinaryTag.empty() ? null : BinaryTagHolder.encode(tag, SNBT_CODEC) - ); + return NEW_INSTANCE.deserializeShowItem(input); } @Override public HoverEvent.@NotNull ShowEntity deserializeShowEntity(final @NotNull Component input, final Codec.Decoder componentCodec) throws IOException { - assertTextComponent(input); - final CompoundBinaryTag contents = SNBT_CODEC.decode(((TextComponent) input).content()); - return HoverEvent.ShowEntity.showEntity( - Key.key(contents.getString(ENTITY_TYPE)), - UUID.fromString(contents.getString(ENTITY_ID)), - componentCodec.decode(contents.getString(ENTITY_NAME)) - ); - } - - private static void assertTextComponent(final Component component) { - if (!(component instanceof TextComponent) || !component.children().isEmpty()) { - throw new IllegalArgumentException("Legacy events must be single Component instances"); - } + return NEW_INSTANCE.deserializeShowEntity(input, componentCodec); } @Override public @NotNull Component serializeShowItem(final HoverEvent.@NotNull ShowItem input) throws IOException { - final CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder() - .putString(ITEM_TYPE, input.item().asString()) - .putByte(ITEM_COUNT, (byte) input.count()); - final @Nullable BinaryTagHolder nbt = input.nbt(); - if (nbt != null) { - builder.put(ITEM_TAG, nbt.get(SNBT_CODEC)); - } - return Component.text(SNBT_CODEC.encode(builder.build())); + return NEW_INSTANCE.serializeShowItem(input); } @Override public @NotNull Component serializeShowEntity(final HoverEvent.@NotNull ShowEntity input, final Codec.Encoder componentCodec) throws IOException { - final CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder() - .putString(ENTITY_ID, input.id().toString()) - .putString(ENTITY_TYPE, input.type().asString()); - final @Nullable Component name = input.name(); - if (name != null) { - builder.putString(ENTITY_NAME, componentCodec.encode(name)); - } - return Component.text(SNBT_CODEC.encode(builder.build())); + return NEW_INSTANCE.serializeShowEntity(input, componentCodec); } } diff --git a/text-serializer-gson/build.gradle.kts b/text-serializer-gson/build.gradle.kts index 04e83fd62..5d6c1935e 100644 --- a/text-serializer-gson/build.gradle.kts +++ b/text-serializer-gson/build.gradle.kts @@ -1,13 +1,10 @@ plugins { - id("adventure.common-conventions") + id("adventure.json-impl-conventions") alias(libs.plugins.jmh) } dependencies { - api(projects.adventureApi) api(libs.gson) - testImplementation(projects.adventureNbt) - annotationProcessor(projects.adventureAnnotationProcessors) } applyJarMetadata("net.kyori.adventure.text.serializer.gson") diff --git a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/ComponentSerializerImpl.java b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/ComponentSerializerImpl.java index 88a1d0ca3..b3bb27732 100644 --- a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/ComponentSerializerImpl.java +++ b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/ComponentSerializerImpl.java @@ -53,25 +53,25 @@ import net.kyori.adventure.text.TranslatableComponent; import org.jetbrains.annotations.Nullable; -final class ComponentSerializerImpl extends TypeAdapter { - static final String TEXT = "text"; - static final String TRANSLATE = "translate"; - static final String TRANSLATE_FALLBACK = "fallback"; - static final String TRANSLATE_WITH = "with"; - static final String SCORE = "score"; - static final String SCORE_NAME = "name"; - static final String SCORE_OBJECTIVE = "objective"; - static final String SCORE_VALUE = "value"; - static final String SELECTOR = "selector"; - static final String KEYBIND = "keybind"; - static final String EXTRA = "extra"; - static final String NBT = "nbt"; - static final String NBT_INTERPRET = "interpret"; - static final String NBT_BLOCK = "block"; - static final String NBT_ENTITY = "entity"; - static final String NBT_STORAGE = "storage"; - static final String SEPARATOR = "separator"; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.EXTRA; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.KEYBIND; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.NBT; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.NBT_BLOCK; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.NBT_ENTITY; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.NBT_INTERPRET; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.NBT_STORAGE; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.SCORE; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.SCORE_NAME; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.SCORE_OBJECTIVE; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.SCORE_VALUE; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.SELECTOR; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.SEPARATOR; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.TEXT; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.TRANSLATE; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.TRANSLATE_FALLBACK; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.TRANSLATE_WITH; +final class ComponentSerializerImpl extends TypeAdapter { static final Type COMPONENT_LIST_TYPE = new TypeToken>() {}.getType(); static TypeAdapter create(final Gson gson) { diff --git a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/GsonComponentSerializer.java b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/GsonComponentSerializer.java index 7290385ca..209bde6a4 100644 --- a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/GsonComponentSerializer.java +++ b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/GsonComponentSerializer.java @@ -30,7 +30,7 @@ import java.util.function.UnaryOperator; import net.kyori.adventure.builder.AbstractBuilder; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.ComponentSerializer; +import net.kyori.adventure.text.serializer.json.JSONComponentSerializer; import net.kyori.adventure.util.Buildable; import net.kyori.adventure.util.PlatformAPI; import org.jetbrains.annotations.ApiStatus; @@ -40,12 +40,15 @@ /** * A gson component serializer. * + *

This is a specific implementation of {@link JSONComponentSerializer} for the Gson library. + * Libraries that want to remain unopinionated should work with that interface instead.

+ * *

Use {@link Builder#downsampleColors()} to support platforms * that do not understand hex colors that were introduced in Minecraft 1.16.

* * @since 4.0.0 */ -public interface GsonComponentSerializer extends ComponentSerializer, Buildable { +public interface GsonComponentSerializer extends JSONComponentSerializer, Buildable { /** * Gets a component serializer for gson serialization and deserialization. * @@ -118,7 +121,7 @@ static Builder builder() { * * @since 4.0.0 */ - interface Builder extends AbstractBuilder, Buildable.Builder { + interface Builder extends AbstractBuilder, Buildable.Builder, JSONComponentSerializer.Builder { /** * Sets that the serializer should downsample hex colors to named colors. * @@ -135,16 +138,19 @@ interface Builder extends AbstractBuilder, Buildable.Bu * @param serializer serializer * @return this builder * @since 4.0.0 + * @deprecated for removal since 4.14.0, use {@link #legacyHoverEventSerializer(net.kyori.adventure.text.serializer.json.LegacyHoverEventSerializer)} instead */ - @NotNull Builder legacyHoverEventSerializer(final @Nullable LegacyHoverEventSerializer serializer); + @Deprecated + default @NotNull Builder legacyHoverEventSerializer(final @Nullable LegacyHoverEventSerializer serializer) { + return this.legacyHoverEventSerializer((net.kyori.adventure.text.serializer.json.LegacyHoverEventSerializer) serializer); + } + + @Override + @NotNull Builder legacyHoverEventSerializer(final net.kyori.adventure.text.serializer.json.@Nullable LegacyHoverEventSerializer serializer); /** - * Output a legacy hover event {@code value} in addition to the modern {@code contents}. + * {@inheritDoc} * - *

A {@link #legacyHoverEventSerializer(LegacyHoverEventSerializer) legacy hover serializer} must also be set - * to serialize any hover events beyond those with action {@link net.kyori.adventure.text.event.HoverEvent.Action#SHOW_TEXT}

- * - * @return this builder * @since 4.0.0 */ @NotNull Builder emitLegacyHoverEvent(); diff --git a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/GsonComponentSerializerImpl.java b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/GsonComponentSerializerImpl.java index a855bbe48..acf1b65f8 100644 --- a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/GsonComponentSerializerImpl.java +++ b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/GsonComponentSerializerImpl.java @@ -55,10 +55,10 @@ static final class Instances { private final Gson serializer; private final UnaryOperator populator; private final boolean downsampleColor; - private final @Nullable LegacyHoverEventSerializer legacyHoverSerializer; + private final net.kyori.adventure.text.serializer.json.@Nullable LegacyHoverEventSerializer legacyHoverSerializer; private final boolean emitLegacyHover; - GsonComponentSerializerImpl(final boolean downsampleColor, final @Nullable LegacyHoverEventSerializer legacyHoverSerializer, final boolean emitLegacyHover) { + GsonComponentSerializerImpl(final boolean downsampleColor, final net.kyori.adventure.text.serializer.json.@Nullable LegacyHoverEventSerializer legacyHoverSerializer, final boolean emitLegacyHover) { this.downsampleColor = downsampleColor; this.legacyHoverSerializer = legacyHoverSerializer; this.emitLegacyHover = emitLegacyHover; @@ -121,7 +121,7 @@ static final class Instances { static final class BuilderImpl implements Builder { private boolean downsampleColor = false; - private @Nullable LegacyHoverEventSerializer legacyHoverSerializer; + private net.kyori.adventure.text.serializer.json.@Nullable LegacyHoverEventSerializer legacyHoverSerializer; private boolean emitLegacyHover = false; BuilderImpl() { @@ -142,7 +142,7 @@ static final class BuilderImpl implements Builder { } @Override - public @NotNull Builder legacyHoverEventSerializer(final @Nullable LegacyHoverEventSerializer serializer) { + public @NotNull Builder legacyHoverEventSerializer(final net.kyori.adventure.text.serializer.json.@Nullable LegacyHoverEventSerializer serializer) { this.legacyHoverSerializer = serializer; return this; } diff --git a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/JSONComponentSerializerProviderImpl.java b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/JSONComponentSerializerProviderImpl.java new file mode 100644 index 000000000..346084a2e --- /dev/null +++ b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/JSONComponentSerializerProviderImpl.java @@ -0,0 +1,48 @@ +/* + * This file is part of adventure, licensed under the MIT License. + * + * Copyright (c) 2017-2023 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.text.serializer.gson; + +import java.util.function.Supplier; +import net.kyori.adventure.text.serializer.json.JSONComponentSerializer; +import net.kyori.adventure.util.Services; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * Implementation of the JSON component serializer provider. + * + * @since 4.14.0 + */ +@ApiStatus.Internal +public final class JSONComponentSerializerProviderImpl implements JSONComponentSerializer.Provider, Services.Fallback { + @Override + public @NotNull JSONComponentSerializer json() { + return GsonComponentSerializer.gson(); + } + + @Override + public @NotNull Supplier builder() { + return GsonComponentSerializer::builder; + } +} diff --git a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/LegacyHoverEventSerializer.java b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/LegacyHoverEventSerializer.java index 73ffffb68..ca7479f1c 100644 --- a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/LegacyHoverEventSerializer.java +++ b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/LegacyHoverEventSerializer.java @@ -23,57 +23,16 @@ */ package net.kyori.adventure.text.serializer.gson; -import java.io.IOException; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.HoverEvent; -import net.kyori.adventure.util.Codec; -import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.ApiStatus; /** * Adapter to convert between modern and legacy hover event formats. * * @since 4.0.0 + * @deprecated for removal since 4.13.0, implement {@link net.kyori.adventure.text.serializer.json.LegacyHoverEventSerializer} instead */ -public interface LegacyHoverEventSerializer { - /** - * Convert a legacy hover event {@code show_item} value to its modern format. - * - * @param input component whose plain-text value is a SNBT string - * @return the deserialized event - * @throws IOException if the input is improperly formatted - * @since 4.0.0 - */ - HoverEvent.@NotNull ShowItem deserializeShowItem(final @NotNull Component input) throws IOException; +@Deprecated +@ApiStatus.ScheduledForRemoval(inVersion = "5.0.0") +public interface LegacyHoverEventSerializer extends net.kyori.adventure.text.serializer.json.LegacyHoverEventSerializer { - /** - * Convert a legacy hover event {@code show_entity} value to its modern format. - * - * @param input component whose plain-text value is a SNBT string - * @param componentDecoder A decoder that can take a JSON string and return a deserialized component - * @return the deserialized event - * @throws IOException if the input is improperly formatted - * @since 4.0.0 - */ - HoverEvent.@NotNull ShowEntity deserializeShowEntity(final @NotNull Component input, final Codec.Decoder componentDecoder) throws IOException; - - /** - * Convert a modern hover event {@code show_item} value to its legacy format. - * - * @param input modern hover event - * @return component with the legacy value as a SNBT string - * @throws IOException if the input is improperly formatted - * @since 4.0.0 - */ - @NotNull Component serializeShowItem(final HoverEvent.@NotNull ShowItem input) throws IOException; - - /** - * Convert a modern hover event {@code show_entity} value to its legacy format. - * - * @param input modern hover event - * @param componentEncoder An encoder that can take a {@link Component} and return a JSON string - * @return component with the legacy value as a SNBT string - * @throws IOException if the input is improperly formatted - * @since 4.0.0 - */ - @NotNull Component serializeShowEntity(final HoverEvent.@NotNull ShowEntity input, final Codec.Encoder componentEncoder) throws IOException; } diff --git a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/SerializerFactory.java b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/SerializerFactory.java index b47be5e0a..e644cf7ea 100644 --- a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/SerializerFactory.java +++ b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/SerializerFactory.java @@ -51,10 +51,10 @@ final class SerializerFactory implements TypeAdapterFactory { static final Class BLOCK_NBT_POS_TYPE = BlockNBTComponent.Pos.class; private final boolean downsampleColors; - private final LegacyHoverEventSerializer legacyHoverSerializer; + private final net.kyori.adventure.text.serializer.json.LegacyHoverEventSerializer legacyHoverSerializer; private final boolean emitLegacyHover; - SerializerFactory(final boolean downsampleColors, final @Nullable LegacyHoverEventSerializer legacyHoverSerializer, final boolean emitLegacyHover) { + SerializerFactory(final boolean downsampleColors, final net.kyori.adventure.text.serializer.json.@Nullable LegacyHoverEventSerializer legacyHoverSerializer, final boolean emitLegacyHover) { this.downsampleColors = downsampleColors; this.legacyHoverSerializer = legacyHoverSerializer; this.emitLegacyHover = emitLegacyHover; diff --git a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/ShowEntitySerializer.java b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/ShowEntitySerializer.java index b8947beaa..bcd9b72c0 100644 --- a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/ShowEntitySerializer.java +++ b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/ShowEntitySerializer.java @@ -35,11 +35,11 @@ import net.kyori.adventure.text.event.HoverEvent; import org.jetbrains.annotations.Nullable; -final class ShowEntitySerializer extends TypeAdapter { - static final String TYPE = "type"; - static final String ID = "id"; - static final String NAME = "name"; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.SHOW_ENTITY_ID; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.SHOW_ENTITY_NAME; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.SHOW_ENTITY_TYPE; +final class ShowEntitySerializer extends TypeAdapter { static TypeAdapter create(final Gson gson) { return new ShowEntitySerializer(gson).nullSafe(); } @@ -60,11 +60,11 @@ public HoverEvent.ShowEntity read(final JsonReader in) throws IOException { while (in.hasNext()) { final String fieldName = in.nextName(); - if (fieldName.equals(TYPE)) { + if (fieldName.equals(SHOW_ENTITY_TYPE)) { type = this.gson.fromJson(in, SerializerFactory.KEY_TYPE); - } else if (fieldName.equals(ID)) { + } else if (fieldName.equals(SHOW_ENTITY_ID)) { id = UUID.fromString(in.nextString()); - } else if (fieldName.equals(NAME)) { + } else if (fieldName.equals(SHOW_ENTITY_NAME)) { name = this.gson.fromJson(in, SerializerFactory.COMPONENT_TYPE); } else { in.skipValue(); @@ -83,15 +83,15 @@ public HoverEvent.ShowEntity read(final JsonReader in) throws IOException { public void write(final JsonWriter out, final HoverEvent.ShowEntity value) throws IOException { out.beginObject(); - out.name(TYPE); + out.name(SHOW_ENTITY_TYPE); this.gson.toJson(value.type(), SerializerFactory.KEY_TYPE, out); - out.name(ID); + out.name(SHOW_ENTITY_ID); out.value(value.id().toString()); final @Nullable Component name = value.name(); if (name != null) { - out.name(NAME); + out.name(SHOW_ENTITY_NAME); this.gson.toJson(name, SerializerFactory.COMPONENT_TYPE, out); } diff --git a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/ShowItemSerializer.java b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/ShowItemSerializer.java index f243c063a..180db185a 100644 --- a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/ShowItemSerializer.java +++ b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/ShowItemSerializer.java @@ -35,11 +35,11 @@ import net.kyori.adventure.text.event.HoverEvent; import org.jetbrains.annotations.Nullable; -final class ShowItemSerializer extends TypeAdapter { - static final String ID = "id"; - static final String COUNT = "count"; - static final String TAG = "tag"; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.SHOW_ITEM_COUNT; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.SHOW_ITEM_ID; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.SHOW_ITEM_TAG; +final class ShowItemSerializer extends TypeAdapter { static TypeAdapter create(final Gson gson) { return new ShowItemSerializer(gson).nullSafe(); } @@ -60,11 +60,11 @@ public HoverEvent.ShowItem read(final JsonReader in) throws IOException { while (in.hasNext()) { final String fieldName = in.nextName(); - if (fieldName.equals(ID)) { + if (fieldName.equals(SHOW_ITEM_ID)) { key = this.gson.fromJson(in, SerializerFactory.KEY_TYPE); - } else if (fieldName.equals(COUNT)) { + } else if (fieldName.equals(SHOW_ITEM_COUNT)) { count = in.nextInt(); - } else if (fieldName.equals(TAG)) { + } else if (fieldName.equals(SHOW_ITEM_TAG)) { final JsonToken token = in.peek(); if (token == JsonToken.STRING || token == JsonToken.NUMBER) { nbt = BinaryTagHolder.binaryTagHolder(in.nextString()); @@ -73,7 +73,7 @@ public HoverEvent.ShowItem read(final JsonReader in) throws IOException { } else if (token == JsonToken.NULL) { in.nextNull(); } else { - throw new JsonParseException("Expected " + TAG + " to be a string"); + throw new JsonParseException("Expected " + SHOW_ITEM_TAG + " to be a string"); } } else { in.skipValue(); @@ -92,18 +92,18 @@ public HoverEvent.ShowItem read(final JsonReader in) throws IOException { public void write(final JsonWriter out, final HoverEvent.ShowItem value) throws IOException { out.beginObject(); - out.name(ID); + out.name(SHOW_ITEM_ID); this.gson.toJson(value.item(), SerializerFactory.KEY_TYPE, out); final int count = value.count(); if (count != 1) { - out.name(COUNT); + out.name(SHOW_ITEM_COUNT); out.value(count); } final @Nullable BinaryTagHolder nbt = value.nbt(); if (nbt != null) { - out.name(TAG); + out.name(SHOW_ITEM_TAG); out.value(nbt.string()); } diff --git a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/StyleSerializer.java b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/StyleSerializer.java index 5f692cb69..4b385785f 100644 --- a/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/StyleSerializer.java +++ b/text-serializer-gson/src/main/java/net/kyori/adventure/text/serializer/gson/StyleSerializer.java @@ -46,6 +46,17 @@ import net.kyori.adventure.util.Codec; import org.jetbrains.annotations.Nullable; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.CLICK_EVENT; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.CLICK_EVENT_ACTION; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.CLICK_EVENT_VALUE; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.COLOR; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.FONT; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.HOVER_EVENT; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.HOVER_EVENT_ACTION; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.HOVER_EVENT_CONTENTS; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.HOVER_EVENT_VALUE; +import static net.kyori.adventure.text.serializer.json.JSONComponentConstants.INSERTION; + final class StyleSerializer extends TypeAdapter