From 2c13c671e51b49a79d07ad50864a30886b0bc4f4 Mon Sep 17 00:00:00 2001 From: Riley Park Date: Wed, 7 Apr 2021 21:17:31 -0700 Subject: [PATCH] api: Expose ComponentLike -> Component conversion method --- .../adventure/text/AbstractComponent.java | 35 +-------- .../kyori/adventure/text/ComponentLike.java | 54 +++++++++++++ .../kyori/adventure/text/ScopedComponent.java | 3 +- .../text/TranslatableComponentImpl.java | 4 +- .../kyori/adventure/text/format/Style.java | 4 +- .../net/kyori/adventure/util/MonkeyBars.java | 78 +++++++++++++++++++ .../net/kyori/adventure/util/ShadyPines.java | 8 +- 7 files changed, 145 insertions(+), 41 deletions(-) create mode 100644 api/src/main/java/net/kyori/adventure/util/MonkeyBars.java diff --git a/api/src/main/java/net/kyori/adventure/text/AbstractComponent.java b/api/src/main/java/net/kyori/adventure/text/AbstractComponent.java index 52c50bd45..bf1d77239 100644 --- a/api/src/main/java/net/kyori/adventure/text/AbstractComponent.java +++ b/api/src/main/java/net/kyori/adventure/text/AbstractComponent.java @@ -23,11 +23,10 @@ */ package net.kyori.adventure.text; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.stream.Stream; import net.kyori.adventure.text.format.Style; import net.kyori.adventure.util.Buildable; @@ -46,34 +45,7 @@ */ @Debug.Renderer(text = "this.debuggerString()", childrenArray = "this.children().toArray()", hasChildren = "!this.children().isEmpty()") public abstract class AbstractComponent implements Component { - static List asComponents(final List list) { - return asComponents(list, false); - } - - static List asComponents(final List list, final boolean allowEmpty) { - if(list.isEmpty()) { - // We do not need to create a new list if the one we are copying is empty - we can - // simply just return our known-empty list instead. - return Collections.emptyList(); - } - final List components = new ArrayList<>(list.size()); - for(int i = 0, size = list.size(); i < size; i++) { - final ComponentLike like = list.get(i); - final Component component = like.asComponent(); - if(allowEmpty || component != Component.empty()) { - components.add(component); - } - } - return Collections.unmodifiableList(components); - } - - static List addOne(final List oldList, final T newElement) { - if(oldList.isEmpty()) return Collections.singletonList(newElement); - final List newList = new ArrayList<>(oldList.size() + 1); - newList.addAll(oldList); - newList.add(newElement); - return newList; - } + private static final Predicate NOT_EMPTY = component -> component != Component.empty(); /** * The list of children. @@ -85,7 +57,7 @@ static List addOne(final List oldList, final T newElement) { protected final Style style; protected AbstractComponent(final @NonNull List children, final @NonNull Style style) { - this.children = asComponents(children); + this.children = ComponentLike.asComponents(children, NOT_EMPTY); this.style = style; } @@ -130,6 +102,7 @@ public int hashCode() { return result; } + @SuppressWarnings("unused") private String debuggerString() { return StringExaminer.simpleEscaping().examine(this.examinableName(), this.examinablePropertiesWithoutChildren()); } diff --git a/api/src/main/java/net/kyori/adventure/text/ComponentLike.java b/api/src/main/java/net/kyori/adventure/text/ComponentLike.java index 18c81b646..0278eb317 100644 --- a/api/src/main/java/net/kyori/adventure/text/ComponentLike.java +++ b/api/src/main/java/net/kyori/adventure/text/ComponentLike.java @@ -23,7 +23,12 @@ */ package net.kyori.adventure.text; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.Contract; /** @@ -33,6 +38,55 @@ */ @FunctionalInterface public interface ComponentLike { + /** + * Converts a list of {@link ComponentLike}s to a list of {@link Component}s. + * + * @param likes the component-likes + * @return the components + * @since 4.8.0 + */ + static @NonNull List asComponents(final @NonNull List likes) { + return asComponents(likes, null); + } + + /** + * Converts a list of {@link ComponentLike}s to a list of {@link Component}s. + * + *

Only components that match {@code filter} will be returned.

+ * + * @param likes the component-likes + * @param filter the component filter + * @return the components + * @since 4.8.0 + */ + static @NonNull List asComponents(final @NonNull List likes, final @Nullable Predicate filter) { + if(likes.isEmpty()) { + // We do not need to create a new list if the one we are copying is empty - we can + // simply just return our known-empty list instead. + return Collections.emptyList(); + } + final int size = likes.size(); + @Nullable ArrayList components = null; + for(int i = 0; i < size; i++) { + final ComponentLike like = likes.get(i); + final Component component = like.asComponent(); + if(filter == null || filter.test(component)) { + if(components == null) { + components = new ArrayList<>(size); + } + components.add(component); + } + } + if(components != null) { + // https://github.com/KyoriPowered/adventure/pull/327#discussion_r631420264 + // we pre-size the list, but filtering might make the actual size much smaller + components.trimToSize(); + } + // if we filtered all elements out, just use an empty list instead + if(components == null) return Collections.emptyList(); + return Collections.unmodifiableList(components); + } + /** * Gets a {@link Component} representation. * diff --git a/api/src/main/java/net/kyori/adventure/text/ScopedComponent.java b/api/src/main/java/net/kyori/adventure/text/ScopedComponent.java index 79890b2f0..0ecc5ff84 100644 --- a/api/src/main/java/net/kyori/adventure/text/ScopedComponent.java +++ b/api/src/main/java/net/kyori/adventure/text/ScopedComponent.java @@ -31,6 +31,7 @@ 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.MonkeyBars; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -76,7 +77,7 @@ public interface ScopedComponent extends Component { default @NonNull C append(final @NonNull Component component) { if(component == Component.empty()) return (C) this; final List oldChildren = this.children(); - return this.children(AbstractComponent.addOne(oldChildren, component)); + return this.children(MonkeyBars.addOne(oldChildren, component)); } @Override diff --git a/api/src/main/java/net/kyori/adventure/text/TranslatableComponentImpl.java b/api/src/main/java/net/kyori/adventure/text/TranslatableComponentImpl.java index 8c8133877..86a38197e 100644 --- a/api/src/main/java/net/kyori/adventure/text/TranslatableComponentImpl.java +++ b/api/src/main/java/net/kyori/adventure/text/TranslatableComponentImpl.java @@ -48,7 +48,7 @@ final class TranslatableComponentImpl extends AbstractComponent implements Trans super(children, style); this.key = requireNonNull(key, "key"); // Since translation arguments can be indexed, empty components are also included. - this.args = AbstractComponent.asComponents(args, true); + this.args = ComponentLike.asComponents(args); } @Override @@ -164,7 +164,7 @@ static final class BuilderImpl extends AbstractComponentBuilder args) { - this.args = AbstractComponent.asComponents(args, true); + this.args = ComponentLike.asComponents(args); return this; } diff --git a/api/src/main/java/net/kyori/adventure/text/format/Style.java b/api/src/main/java/net/kyori/adventure/text/format/Style.java index 547a70b26..54a79e7e3 100644 --- a/api/src/main/java/net/kyori/adventure/text/format/Style.java +++ b/api/src/main/java/net/kyori/adventure/text/format/Style.java @@ -34,7 +34,7 @@ import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.event.HoverEventSource; import net.kyori.adventure.util.Buildable; -import net.kyori.adventure.util.ShadyPines; +import net.kyori.adventure.util.MonkeyBars; import net.kyori.examination.Examinable; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -577,7 +577,7 @@ enum Merge { * @since 4.0.0 */ public static @Unmodifiable @NonNull Set of(final Merge@NonNull... merges) { - return ShadyPines.enumSet(Merge.class, merges); + return MonkeyBars.enumSet(Merge.class, merges); } static boolean hasAll(final @NonNull Set merges) { diff --git a/api/src/main/java/net/kyori/adventure/util/MonkeyBars.java b/api/src/main/java/net/kyori/adventure/util/MonkeyBars.java new file mode 100644 index 000000000..cbdb84663 --- /dev/null +++ b/api/src/main/java/net/kyori/adventure/util/MonkeyBars.java @@ -0,0 +1,78 @@ +/* + * 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.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * {@link Collection} related utilities. + * + * @since 4.8.0 + */ +public final class MonkeyBars { + private MonkeyBars() { + } + + /** + * Creates a set from an array of enum constants. + * + * @param type the enum type + * @param constants the enum constants + * @param the enum type + * @return the set + * @since 4.0.0 + */ + @SafeVarargs + @SuppressWarnings("varargs") + public static > @NonNull Set enumSet(final Class type, final E@NonNull... constants) { + final Set set = EnumSet.noneOf(type); + Collections.addAll(set, constants); + return Collections.unmodifiableSet(set); + } + + /** + * Adds an element to the end of the list, or returns a new list. + * + *

The returned list is unmodifiable.

+ * + * @param oldList the old list + * @param newElement the element to add + * @param the element type + * @return a list + * @since 4.8.0 + */ + public static @NonNull List addOne(final @NonNull List oldList, final T newElement) { + if(oldList.isEmpty()) return Collections.singletonList(newElement); + final List newList = new ArrayList<>(oldList.size() + 1); + newList.addAll(oldList); + newList.add(newElement); + return Collections.unmodifiableList(newList); + } +} diff --git a/api/src/main/java/net/kyori/adventure/util/ShadyPines.java b/api/src/main/java/net/kyori/adventure/util/ShadyPines.java index 54653b0b6..37aaabc2e 100644 --- a/api/src/main/java/net/kyori/adventure/util/ShadyPines.java +++ b/api/src/main/java/net/kyori/adventure/util/ShadyPines.java @@ -23,8 +23,6 @@ */ package net.kyori.adventure.util; -import java.util.Collections; -import java.util.EnumSet; import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; @@ -44,14 +42,14 @@ private ShadyPines() { * @param constants the enum constants * @param the enum type * @return the set + * @deprecated for removal since 4.8.0, use {@link MonkeyBars#enumSet(Class, Enum[])} * @since 4.0.0 */ + @Deprecated @SafeVarargs @SuppressWarnings("varargs") public static > @NonNull Set enumSet(final Class type, final E@NonNull... constants) { - final Set set = EnumSet.noneOf(type); - Collections.addAll(set, constants); - return Collections.unmodifiableSet(set); + return MonkeyBars.enumSet(type, constants); } /**