diff --git a/api/src/main/java/net/kyori/adventure/util/Services.java b/api/src/main/java/net/kyori/adventure/util/Services.java
new file mode 100644
index 0000000000..cbfab07ed1
--- /dev/null
+++ b/api/src/main/java/net/kyori/adventure/util/Services.java
@@ -0,0 +1,64 @@
+/*
+ * This file is part of adventure, licensed under the MIT License.
+ *
+ * Copyright (c) 2017-2021 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.util;
+
+import java.util.Iterator;
+import java.util.Optional;
+import java.util.ServiceLoader;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+/**
+ * Tools for working with {@link ServiceLoader}s.
+ *
+ * @since 4.8.0
+ */
+public final class Services {
+ private Services() {
+ }
+
+ /**
+ * Locates a service provider.
+ *
+ * @param type the service provider type
+ * @param
the service provider type
+ * @return a service
+ * @since 4.8.0
+ */
+ public static
Optional
service(final @NonNull Class
type) {
+ final ServiceLoader
loader = Services0.loader(type);
+ final Iterator
it = loader.iterator();
+ while(it.hasNext()) {
+ try {
+ final P provider = it.next();
+ if(it.hasNext()) {
+ throw new IllegalStateException("Expected to find one " + type + " service provider, found multiple");
+ }
+ return Optional.of(provider);
+ } catch(final Throwable t) {
+ // ignored
+ }
+ }
+ return Optional.empty();
+ }
+}
diff --git a/api/src/main/java/net/kyori/adventure/util/Services0.java b/api/src/main/java/net/kyori/adventure/util/Services0.java
new file mode 100644
index 0000000000..20918e55e6
--- /dev/null
+++ b/api/src/main/java/net/kyori/adventure/util/Services0.java
@@ -0,0 +1,35 @@
+/*
+ * This file is part of adventure, licensed under the MIT License.
+ *
+ * Copyright (c) 2017-2021 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.util;
+
+import java.util.ServiceLoader;
+
+final class Services0 {
+ private Services0() {
+ }
+
+ static ServiceLoader loader(final Class type) {
+ return ServiceLoader.load(type, type.getClassLoader());
+ }
+}
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 e8772c7dcf..e86e28f6f4 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
@@ -32,6 +32,7 @@
import net.kyori.adventure.util.Buildable;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jetbrains.annotations.ApiStatus;
/**
* A gson component serializer.
@@ -153,4 +154,30 @@ interface Builder extends Buildable.Builder {
@Override
@NonNull GsonComponentSerializer build();
}
+
+ /**
+ * A {@link GsonComponentSerializer} service provider.
+ *
+ * @since 4.8.0
+ */
+ @ApiStatus.Internal
+ interface Provider {
+ /**
+ * Provides a standard {@link GsonComponentSerializer}.
+ *
+ * @return a {@link GsonComponentSerializer}
+ * @since 4.8.0
+ */
+ @ApiStatus.Internal
+ @NonNull GsonComponentSerializer gson();
+
+ /**
+ * Provides a legacy {@link GsonComponentSerializer}.
+ *
+ * @return a {@link GsonComponentSerializer}
+ * @since 4.8.0
+ */
+ @ApiStatus.Internal
+ @NonNull GsonComponentSerializer gsonLegacy();
+ }
}
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 785d2f0c29..57f6fad4ef 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
@@ -26,6 +26,7 @@
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
+import java.util.Optional;
import java.util.function.UnaryOperator;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.BlockNBTComponent;
@@ -35,12 +36,18 @@
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
+import net.kyori.adventure.util.Services;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
final class GsonComponentSerializerImpl implements GsonComponentSerializer {
- static final GsonComponentSerializer INSTANCE = new GsonComponentSerializerImpl(false, null, false);
- static final GsonComponentSerializer LEGACY_INSTANCE = new GsonComponentSerializerImpl(true, null, true);
+ private static final Optional SERVICE = Services.service(Provider.class);
+ static final GsonComponentSerializer INSTANCE = SERVICE
+ .map(Provider::gson)
+ .orElseGet(() -> new GsonComponentSerializerImpl(false, null, false));
+ static final GsonComponentSerializer LEGACY_INSTANCE = SERVICE
+ .map(Provider::gsonLegacy)
+ .orElseGet(() -> new GsonComponentSerializerImpl(true, null, true));
private final Gson serializer;
private final UnaryOperator populator;
diff --git a/text-serializer-legacy/src/main/java/net/kyori/adventure/text/serializer/legacy/LegacyComponentSerializer.java b/text-serializer-legacy/src/main/java/net/kyori/adventure/text/serializer/legacy/LegacyComponentSerializer.java
index 350fd0dc4c..77cfa1c7d7 100644
--- a/text-serializer-legacy/src/main/java/net/kyori/adventure/text/serializer/legacy/LegacyComponentSerializer.java
+++ b/text-serializer-legacy/src/main/java/net/kyori/adventure/text/serializer/legacy/LegacyComponentSerializer.java
@@ -23,17 +23,19 @@
*/
package net.kyori.adventure.text.serializer.legacy;
+import java.util.function.Consumer;
import java.util.regex.Pattern;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
-import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.flattener.ComponentFlattener;
+import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.serializer.ComponentSerializer;
import net.kyori.adventure.util.Buildable;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jetbrains.annotations.ApiStatus;
/**
* A legacy component serializer.
@@ -261,4 +263,39 @@ interface Builder extends Buildable.Builder {
@Override
@NonNull LegacyComponentSerializer build();
}
+
+ /**
+ * A {@link LegacyComponentSerializer} service provider.
+ *
+ * @since 4.8.0
+ */
+ @ApiStatus.Internal
+ interface Provider {
+ /**
+ * Provides a {@link LegacyComponentSerializer} using {@link #AMPERSAND_CHAR}.
+ *
+ * @return a {@link LegacyComponentSerializer}
+ * @since 4.8.0
+ */
+ @ApiStatus.Internal
+ @NonNull LegacyComponentSerializer legacyAmpersand();
+
+ /**
+ * Provides a {@link LegacyComponentSerializer} using {@link #SECTION_CHAR}.
+ *
+ * @return a {@link LegacyComponentSerializer}
+ * @since 4.8.0
+ */
+ @ApiStatus.Internal
+ @NonNull LegacyComponentSerializer legacySection();
+
+ /**
+ * Completes the building process of {@link Builder}.
+ *
+ * @return a {@link Consumer}
+ * @since 4.8.0
+ */
+ @ApiStatus.Internal
+ @NonNull Consumer legacy();
+ }
}
diff --git a/text-serializer-legacy/src/main/java/net/kyori/adventure/text/serializer/legacy/LegacyComponentSerializerImpl.java b/text-serializer-legacy/src/main/java/net/kyori/adventure/text/serializer/legacy/LegacyComponentSerializerImpl.java
index 4017faf704..de9d68eaf7 100644
--- a/text-serializer-legacy/src/main/java/net/kyori/adventure/text/serializer/legacy/LegacyComponentSerializerImpl.java
+++ b/text-serializer-legacy/src/main/java/net/kyori/adventure/text/serializer/legacy/LegacyComponentSerializerImpl.java
@@ -30,7 +30,9 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
+import java.util.function.Consumer;
import java.util.regex.Pattern;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
@@ -43,6 +45,7 @@
import net.kyori.adventure.text.format.TextFormat;
import net.kyori.adventure.text.flattener.ComponentFlattener;
import net.kyori.adventure.text.flattener.FlattenerListener;
+import net.kyori.adventure.util.Services;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -95,8 +98,18 @@ final class LegacyComponentSerializerImpl implements LegacyComponentSerializer {
}
}
- static final LegacyComponentSerializer SECTION_SERIALIZER = new LegacyComponentSerializerImpl(SECTION_CHAR, HEX_CHAR, null, false, false, ComponentFlattener.basic());
- static final LegacyComponentSerializer AMPERSAND_SERIALIZER = new LegacyComponentSerializerImpl(AMPERSAND_CHAR, HEX_CHAR, null, false, false, ComponentFlattener.basic());
+ private static final Optional SERVICE = Services.service(Provider.class);
+ static final LegacyComponentSerializer SECTION_SERIALIZER = SERVICE
+ .map(Provider::legacySection)
+ .orElseGet(() -> new LegacyComponentSerializerImpl(SECTION_CHAR, HEX_CHAR, null, false, false, ComponentFlattener.basic()));
+ static final LegacyComponentSerializer AMPERSAND_SERIALIZER = SERVICE
+ .map(Provider::legacyAmpersand)
+ .orElseGet(() -> new LegacyComponentSerializerImpl(AMPERSAND_CHAR, HEX_CHAR, null, false, false, ComponentFlattener.basic()));
+ static final Consumer BUILDER = SERVICE
+ .map(Provider::legacy)
+ .orElseGet(() -> builder -> {
+ // NOOP
+ });
private final char character;
private final char hexCharacter;
@@ -471,6 +484,7 @@ static final class BuilderImpl implements Builder {
private ComponentFlattener flattener = ComponentFlattener.basic();
BuilderImpl() {
+ BUILDER.accept(this); // let service provider touch the builder before anybody else touches it
}
BuilderImpl(final @NonNull LegacyComponentSerializerImpl serializer) {
diff --git a/text-serializer-plain/src/main/java/net/kyori/adventure/text/serializer/plain/PlainComponentSerializer.java b/text-serializer-plain/src/main/java/net/kyori/adventure/text/serializer/plain/PlainComponentSerializer.java
index e4e9a652ea..7c149cdd86 100644
--- a/text-serializer-plain/src/main/java/net/kyori/adventure/text/serializer/plain/PlainComponentSerializer.java
+++ b/text-serializer-plain/src/main/java/net/kyori/adventure/text/serializer/plain/PlainComponentSerializer.java
@@ -36,40 +36,42 @@
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
-import static java.util.Objects.requireNonNull;
-
/**
* A plain component serializer.
*
* Plain does not support more complex features such as, but not limited
* to, colours, decorations, {@link ClickEvent}, and {@link HoverEvent}.
*
+ * @deprecated for removal since 4.8.0, use {@link PlainTextComponentSerializer} instead
* @since 4.0.0
*/
+@Deprecated
public class PlainComponentSerializer implements ComponentSerializer, Buildable {
- private static final PlainComponentSerializer INSTANCE = builder().build();
-
/**
* A component serializer for plain-based serialization and deserialization.
*
* @return serializer instance
+ * @deprecated for removal since 4.8.0, use {@link PlainTextComponentSerializer#plainText()} instead
* @since 4.0.0
*/
+ @Deprecated
public static @NonNull PlainComponentSerializer plain() {
- return INSTANCE;
+ return PlainComponentSerializerImpl.INSTANCE;
}
/**
* Create a new builder.
*
* @return a new plain serializer builder
+ * @deprecated for removal since 4.8.0, use {@link PlainTextComponentSerializer#builder()} instead
* @since 4.7.0
*/
+ @Deprecated
public static PlainComponentSerializer.@NonNull Builder builder() {
return new PlainComponentSerializerImpl.BuilderImpl();
}
- private final ComponentFlattener flattener;
+ final PlainTextComponentSerializer serializer;
/**
* Constructs.
@@ -79,7 +81,7 @@ public class PlainComponentSerializer implements ComponentSerializer keybind, final @Nullable Function translatable) {
- final ComponentFlattener.Builder builder = ComponentFlattener.basic().toBuilder();
- if(keybind != null) builder.mapper(KeybindComponent.class, keybind);
- if(translatable != null) builder.mapper(TranslatableComponent.class, translatable);
- this.flattener = builder.build();
+ this(PlainComponentSerializerImpl.newSerializerForLegacyFunctions(keybind, translatable));
}
- PlainComponentSerializer(final @NonNull ComponentFlattener flattener) {
- this.flattener = flattener;
+ PlainComponentSerializer(final @NonNull PlainTextComponentSerializer serializer) {
+ this.serializer = serializer;
}
@Override
public @NonNull TextComponent deserialize(final @NonNull String input) {
- return Component.text(input);
+ return this.serializer.deserialize(input);
}
@Override
public @NonNull String serialize(final @NonNull Component component) {
- final StringBuilder sb = new StringBuilder();
- this.serialize(sb, component);
- return sb.toString();
+ return this.serializer.serialize(component);
}
/**
@@ -119,25 +116,27 @@ public PlainComponentSerializer(final @Nullable Function {
-
/**
* Set the component flattener to use.
*
@@ -145,9 +144,10 @@ public interface Builder extends Buildable.Builder {
*
* @param flattener the new flattener
* @return this builder
+ * @deprecated for removal since 4.8.0, use {@link PlainTextComponentSerializer.Builder#flattener(ComponentFlattener)} instead
* @since 4.7.0
*/
+ @Deprecated
@NonNull Builder flattener(final @NonNull ComponentFlattener flattener);
-
}
}
diff --git a/text-serializer-plain/src/main/java/net/kyori/adventure/text/serializer/plain/PlainComponentSerializerImpl.java b/text-serializer-plain/src/main/java/net/kyori/adventure/text/serializer/plain/PlainComponentSerializerImpl.java
index b86bbf24c7..818bbc4638 100644
--- a/text-serializer-plain/src/main/java/net/kyori/adventure/text/serializer/plain/PlainComponentSerializerImpl.java
+++ b/text-serializer-plain/src/main/java/net/kyori/adventure/text/serializer/plain/PlainComponentSerializerImpl.java
@@ -23,37 +23,52 @@
*/
package net.kyori.adventure.text.serializer.plain;
+import java.util.function.Function;
+import net.kyori.adventure.text.KeybindComponent;
+import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.flattener.ComponentFlattener;
import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
-import static java.util.Objects.requireNonNull;
-
+@Deprecated
final class PlainComponentSerializerImpl {
+ static final PlainComponentSerializer INSTANCE = new PlainComponentSerializer();
+
+ private PlainComponentSerializerImpl() {
+ }
+
+ static PlainTextComponentSerializer newSerializerForLegacyFunctions(
+ final @Nullable Function keybind,
+ final @Nullable Function translatable
+ ) {
+ if(keybind == null && translatable == null) {
+ return PlainTextComponentSerializer.plainText();
+ }
+ final ComponentFlattener.Builder builder = ComponentFlattener.basic().toBuilder();
+ if(keybind != null) builder.mapper(KeybindComponent.class, keybind);
+ if(translatable != null) builder.mapper(TranslatableComponent.class, translatable);
+ return PlainTextComponentSerializer.builder().flattener(builder.build()).build();
+ }
static final class BuilderImpl implements PlainComponentSerializer.Builder {
- private ComponentFlattener flattener;
+ private final PlainTextComponentSerializer.Builder builder = PlainTextComponentSerializer.builder();
BuilderImpl() {
- this.flattener = ComponentFlattener.basic().toBuilder()
- .unknownMapper(comp -> {
- throw new UnsupportedOperationException("Don't know how to turn " + comp.getClass().getSimpleName() + " into a string");
- })
- .build();
}
- BuilderImpl(final ComponentFlattener flattener) {
- this.flattener = flattener;
+ BuilderImpl(final PlainComponentSerializer serializer) {
+ this.builder.flattener(((PlainTextComponentSerializerImpl) serializer.serializer).flattener);
}
@Override
public PlainComponentSerializer.@NonNull Builder flattener(final @NonNull ComponentFlattener flattener) {
- this.flattener = requireNonNull(flattener, "flattener");
+ this.builder.flattener(flattener);
return this;
}
@Override
public @NonNull PlainComponentSerializer build() {
- return new PlainComponentSerializer(this.flattener);
+ return new PlainComponentSerializer(this.builder.build());
}
}
}
diff --git a/text-serializer-plain/src/main/java/net/kyori/adventure/text/serializer/plain/PlainTextComponentSerializer.java b/text-serializer-plain/src/main/java/net/kyori/adventure/text/serializer/plain/PlainTextComponentSerializer.java
new file mode 100644
index 0000000000..695cc72cab
--- /dev/null
+++ b/text-serializer-plain/src/main/java/net/kyori/adventure/text/serializer/plain/PlainTextComponentSerializer.java
@@ -0,0 +1,130 @@
+/*
+ * This file is part of adventure, licensed under the MIT License.
+ *
+ * Copyright (c) 2017-2021 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.plain;
+
+import java.util.function.Consumer;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.TextComponent;
+import net.kyori.adventure.text.event.ClickEvent;
+import net.kyori.adventure.text.event.HoverEvent;
+import net.kyori.adventure.text.flattener.ComponentFlattener;
+import net.kyori.adventure.text.serializer.ComponentSerializer;
+import net.kyori.adventure.util.Buildable;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * A plain-text component serializer.
+ *
+ * Plain does not support more complex features such as, but not limited
+ * to, colours, decorations, {@link ClickEvent}, and {@link HoverEvent}.
+ *
+ * @since 4.8.0
+ */
+public interface PlainTextComponentSerializer extends ComponentSerializer, Buildable {
+ /**
+ * A component serializer for plain-based serialization and deserialization.
+ *
+ * @return serializer instance
+ * @since 4.8.0
+ */
+ static @NonNull PlainTextComponentSerializer plainText() {
+ return PlainTextComponentSerializerImpl.INSTANCE;
+ }
+
+ /**
+ * Create a new builder.
+ *
+ * @return a new plain serializer builder
+ * @since 4.8.0
+ */
+ static PlainTextComponentSerializer.@NonNull Builder builder() {
+ return new PlainTextComponentSerializerImpl.BuilderImpl();
+ }
+
+ @Override
+ default @NonNull TextComponent deserialize(final @NonNull String input) {
+ return Component.text(input);
+ }
+
+ @Override
+ default @NonNull String serialize(final @NonNull Component component) {
+ final StringBuilder sb = new StringBuilder();
+ this.serialize(sb, component);
+ return sb.toString();
+ }
+
+ /**
+ * Serializes.
+ *
+ * @param sb the string builder
+ * @param component the component
+ * @since 4.8.0
+ */
+ void serialize(final @NonNull StringBuilder sb, final @NonNull Component component);
+
+ /**
+ * A builder for the plain-text component serializer.
+ *
+ * @since 4.8.0
+ */
+ interface Builder extends Buildable.Builder {
+ /**
+ * Set the component flattener to use.
+ *
+ * The default flattener is {@link ComponentFlattener#basic()} modified to throw exceptions on unknown component types.
+ *
+ * @param flattener the new flattener
+ * @return this builder
+ * @since 4.8.0
+ */
+ @NonNull Builder flattener(final @NonNull ComponentFlattener flattener);
+ }
+
+ /**
+ * A {@link PlainTextComponentSerializer} service provider.
+ *
+ * @since 4.8.0
+ */
+ @ApiStatus.Internal
+ interface Provider {
+ /**
+ * Provides a {@link PlainTextComponentSerializer}.
+ *
+ * @return a {@link PlainTextComponentSerializer}
+ * @since 4.8.0
+ */
+ @ApiStatus.Internal
+ @NonNull PlainTextComponentSerializer plainTextSimple();
+
+ /**
+ * Completes the building process of {@link Builder}.
+ *
+ * @return a {@link Consumer}
+ * @since 4.8.0
+ */
+ @ApiStatus.Internal
+ @NonNull Consumer plainText();
+ }
+}
diff --git a/text-serializer-plain/src/main/java/net/kyori/adventure/text/serializer/plain/PlainTextComponentSerializerImpl.java b/text-serializer-plain/src/main/java/net/kyori/adventure/text/serializer/plain/PlainTextComponentSerializerImpl.java
new file mode 100644
index 0000000000..3b317f291a
--- /dev/null
+++ b/text-serializer-plain/src/main/java/net/kyori/adventure/text/serializer/plain/PlainTextComponentSerializerImpl.java
@@ -0,0 +1,88 @@
+/*
+ * This file is part of adventure, licensed under the MIT License.
+ *
+ * Copyright (c) 2017-2021 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.plain;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.flattener.ComponentFlattener;
+import net.kyori.adventure.util.Services;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+import static java.util.Objects.requireNonNull;
+
+final class PlainTextComponentSerializerImpl implements PlainTextComponentSerializer {
+ private static final ComponentFlattener DEFAULT_FLATTENER = ComponentFlattener.basic().toBuilder()
+ .unknownMapper(component -> {
+ throw new UnsupportedOperationException("Don't know how to turn " + component.getClass().getSimpleName() + " into a string");
+ })
+ .build();
+ private static final Optional SERVICE = Services.service(Provider.class);
+ static final PlainTextComponentSerializer INSTANCE = SERVICE
+ .map(Provider::plainTextSimple)
+ .orElseGet(() -> new PlainTextComponentSerializerImpl(DEFAULT_FLATTENER));
+ static final Consumer BUILDER = SERVICE
+ .map(Provider::plainText)
+ .orElseGet(() -> builder -> {
+ // NOOP
+ });
+ final ComponentFlattener flattener;
+
+ PlainTextComponentSerializerImpl(final ComponentFlattener flattener) {
+ this.flattener = flattener;
+ }
+
+ @Override
+ public void serialize(final @NonNull StringBuilder sb, final @NonNull Component component) {
+ this.flattener.flatten(requireNonNull(component, "component"), sb::append);
+ }
+
+ @Override
+ public @NonNull Builder toBuilder() {
+ return new BuilderImpl(this);
+ }
+
+ static final class BuilderImpl implements PlainTextComponentSerializer.Builder {
+ private ComponentFlattener flattener = DEFAULT_FLATTENER;
+
+ BuilderImpl() {
+ BUILDER.accept(this);
+ }
+
+ BuilderImpl(final PlainTextComponentSerializerImpl serializer) {
+ this.flattener = serializer.flattener;
+ }
+
+ @Override
+ public PlainTextComponentSerializer.@NonNull Builder flattener(final @NonNull ComponentFlattener flattener) {
+ this.flattener = requireNonNull(flattener, "flattener");
+ return this;
+ }
+
+ @Override
+ public @NonNull PlainTextComponentSerializer build() {
+ return new PlainTextComponentSerializerImpl(this.flattener);
+ }
+ }
+}
diff --git a/text-serializer-plain/src/test/java/net/kyori/adventure/text/serializer/plain/PlainComponentSerializerTest.java b/text-serializer-plain/src/test/java/net/kyori/adventure/text/serializer/plain/PlainTextComponentSerializerTest.java
similarity index 88%
rename from text-serializer-plain/src/test/java/net/kyori/adventure/text/serializer/plain/PlainComponentSerializerTest.java
rename to text-serializer-plain/src/test/java/net/kyori/adventure/text/serializer/plain/PlainTextComponentSerializerTest.java
index 49fb077e6c..a17a9d598a 100644
--- a/text-serializer-plain/src/test/java/net/kyori/adventure/text/serializer/plain/PlainComponentSerializerTest.java
+++ b/text-serializer-plain/src/test/java/net/kyori/adventure/text/serializer/plain/PlainTextComponentSerializerTest.java
@@ -31,10 +31,10 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
-class PlainComponentSerializerTest {
+class PlainTextComponentSerializerTest {
@Test
void testSimpleFrom() {
- assertEquals(Component.text("foo"), PlainComponentSerializer.plain().deserialize("foo"));
+ assertEquals(Component.text("foo"), PlainTextComponentSerializer.plainText().deserialize("foo"));
}
@Test
@@ -52,7 +52,7 @@ void testToLegacy() {
)
.append(Component.text("baz"))
.build();
- assertEquals("hifoobarbaz", PlainComponentSerializer.plain().serialize(c1));
+ assertEquals("hifoobarbaz", PlainTextComponentSerializer.plainText().serialize(c1));
final TextComponent c2 = Component.text().content("Hello there, ")
.decoration(TextDecoration.BOLD, TextDecoration.State.TRUE)
@@ -66,6 +66,6 @@ void testToLegacy() {
.color(NamedTextColor.BLUE)
)
.build();
- assertEquals("Hello there, you!", PlainComponentSerializer.plain().serialize(c2));
+ assertEquals("Hello there, you!", PlainTextComponentSerializer.plainText().serialize(c2));
}
}