at.pcgamingfreaks.Message.MessageComponent.java Source code

Java tutorial

Introduction

Here is the source code for at.pcgamingfreaks.Message.MessageComponent.java

Source

/*
 *   Copyright (C) 2016 GeorgH93
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package at.pcgamingfreaks.Message;

import com.google.gson.*;

import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

@SuppressWarnings("unchecked")
public abstract class MessageComponent<T extends MessageComponent, STYLES extends Enum>
        implements JsonDeserializer<T> {
    //region JSON Variables
    protected MessageClickEvent clickEvent = null;
    protected MessageHoverEvent hoverEvent = null;
    protected String text, insertion;
    protected MessageColor color;
    protected Boolean bold, italic, underlined, strikethrough, obfuscated;
    protected List<T> extra = null;

    @SuppressWarnings("unused")
    protected Object selector, score, translate; // We don't use them now, maybe later
    @SuppressWarnings("unused")
    protected List<Object> with; // Only for translate
    //endregion

    /**
     * Gets the JSON string of the component.
     *
     * @return The JSON string of the component.
     */
    @Override
    public String toString() {
        return GSON.toJson(this);
    }

    //region Constructors
    /**
     * Creates a new empty MessageComponent instance.
     */
    protected MessageComponent() {
    }

    /**
     * Creates a new empty MessageComponent instance.
     *
     * @param text   The text for the {@link MessageComponent}.
     * @param styles The style for the {@link MessageComponent}.
     */
    protected MessageComponent(String text, MessageColor... styles) {
        setText(text);
        if (styles != null)
            setStyles(styles);
    }

    /**
     * Creates a new empty MessageComponent instance.
     *
     * @param text   The text for the {@link MessageComponent}.
     * @param styles The style for the {@link MessageComponent}.
     */
    protected MessageComponent(String text, @Nullable STYLES[] styles) {
        setText(text);
        if (styles != null)
            setStyles(styles);
    }
    //endregion

    //region converting JSON message into a classic mc chat message
    /**
     * Converts the JSON message component into a classic chat message.
     *
     * @return The JSON message component in the classic chat format.
     */
    public String getClassicMessage() {
        StringBuilder stringBuilder = new StringBuilder(getClassicFormats());
        if (text != null)
            stringBuilder.append(text);
        if (extra != null) {
            for (MessageComponent e : extra) {
                stringBuilder.append(e.getClassicMessage());
            }
        }
        stringBuilder.append(MessageColor.RESET);
        return stringBuilder.toString();
    }

    /**
     * Converts a {@link Collection} of MessageComponent's into a classic chat message.
     *
     * @param messageList The message components that should be converted into a classic minecraft chat message.
     * @return The JSON message component in the classic chat format.
     */
    public static String getClassicMessage(Collection<? extends MessageComponent> messageList) {
        if (messageList == null)
            return ""; // If we don't have a JSON we can't calculate the classic message from it so we will use an empty message
        StringBuilder builder = new StringBuilder();
        boolean first = true;
        String classFormat = "";
        for (MessageComponent messageComponent : messageList) {
            if (first) // The first json objects format data is used for every following component (as long as they don't set their own stuff)
            {
                classFormat = messageComponent.getClassicFormats(); // We have to store the classic format from the first element
                first = false;
            } else {
                builder.append(classFormat); // Now we append the classic format to our string, followed by the real component
            }
            builder.append(messageComponent.getClassicMessage());
        }
        return builder.toString();
    }

    /**
     * Gets the formats of the component in the classic way ([color/style-code]).
     *
     * @return The classic format string of the component.
     */
    public String getClassicFormats() {
        StringBuilder stringBuilder = new StringBuilder();
        if (isBold())
            stringBuilder.append(MessageColor.BOLD);
        if (isItalic())
            stringBuilder.append(MessageColor.ITALIC);
        if (isObfuscated())
            stringBuilder.append(MessageColor.MAGIC);
        if (isUnderlined())
            stringBuilder.append(MessageColor.UNDERLINE);
        if (isStrikethrough())
            stringBuilder.append(MessageColor.STRIKETHROUGH);
        if (color != null)
            stringBuilder.append(getColor().toString().toLowerCase());
        return stringBuilder.toString();
    }
    //endregion

    //region Message modifier (getter/setter)
    /**
     * Gets the click event of the component.
     *
     * @return The click event of the component.
     */
    public MessageClickEvent getClickEvent() {
        return clickEvent;
    }

    /**
     * Sets the click event of the component.
     * The click event gets used when the client clicks the component.
     *
     * @param clickEvent The click event for the component.
     * @return This message component instance.
     */
    public T setClickEvent(MessageClickEvent clickEvent) {
        this.clickEvent = clickEvent;
        return (T) this;
    }

    /**
     * Gets the hover event of the component.
     *
     * @return The hover event of the component.
     */
    public MessageHoverEvent getHoverEvent() {
        return hoverEvent;
    }

    /**
     * Sets the hover event of the component.
     * The hover event gets used when the client hovers over the component.
     *
     * @param hoverEvent The hover event for the component.
     * @return This message component instance.
     */
    public T setHoverEvent(MessageHoverEvent hoverEvent) {
        this.hoverEvent = hoverEvent;
        return (T) this;
    }

    /**
     * Gets the text of the component.
     *
     * @return The text of the component.
     */
    public String getText() {
        return text;
    }

    /**
     * Sets the text of the component.
     *
     * @param text The new text of the component.
     * @return This message component instance.
     */
    public T setText(String text) {
        this.text = text;
        return (T) this;
    }

    /**
     * Gets the color of the component.
     *
     * @return The color of the component as a {@link String}, null if no color is defined.
     */
    public MessageColor getColor() {
        return color;
    }

    /**
     * Gets the color of the component.
     *
     * @return The color of the component as a {@link MessageColor}, null if no color is defined.
     */
    public String getColorString() {
        return getColor().name().toLowerCase();
    }

    /**
     * Sets the color of the component.
     *
     * @param color The new color of the component.
     * @return This message component instance.
     */
    public T setColor(String color) {
        return setColor(MessageColor.valueOf(color.toUpperCase()));
    }

    /**
     * Sets the color of the component.
     *
     * @param color The new color of the component.
     * @return This message component instance.
     * @exception IllegalArgumentException If the specified {@code ChatColor} enumeration value is not a color (but a format value).
     */
    public T setColor(MessageColor color) throws IllegalArgumentException {
        if (!color.isColor()) {
            throw new IllegalArgumentException(color.name() + " is not a color");
        }
        this.color = color;
        return (T) this;
    }

    /**
     * Sets the color of the component.
     * Use {@link MessageColor} for better performance!
     *
     * @param color The new color of the component.
     * @return This message component instance.
     * @exception IllegalArgumentException If the specified {@code ChatColor} enumeration value is not a color (but a format value).
     */
    public T setColor(STYLES color) throws IllegalArgumentException {
        return setColor(MessageColor.messageColorFromStyle(color));
    }

    /**
     * Gets the behavior of the component if it gets shift-clicked.
     *
     * @return null if nothing should happen, otherwise the text that will be added to the players chat bar.
     */
    public String getInsertion() {
        return insertion;
    }

    /**
     * Set the behavior of the component to instruct the client to append the chat input box content with the specified string when the component is shift-clicked.
     * The client will not immediately send the command to the server to be executed unless the client player submits the command/chat message, usually with the enter key.
     *
     * @param insertionText The text to append to the chat bar of the client.
     * @return This message component instance.
     */
    public T setInsertion(String insertionText) {
        insertion = (insertionText.length() > 100) ? insertionText.substring(0, 100) : insertionText;
        return (T) this;
    }

    /**
     * Checks if the component is bold.
     *
     * @return true if the component is bold, false if not.
     */
    public boolean isBold() {
        return bold != null && bold;
    }

    /**
     * Sets the component to be bold.
     *
     * @return This message component instance.
     */
    public T setBold() {
        return setBold(true);
    }

    /**
     * Sets the component to be bold.
     *
     * @param bold Defines if the component should or should not be bold.
     * @return This message component instance.
     */
    public T setBold(boolean bold) {
        this.bold = bold;
        return (T) this;
    }

    /**
     * Checks if the component is italic.
     *
     * @return true if the component is italic, false if not.
     */
    public boolean isItalic() {
        return italic != null && italic;
    }

    /**
     * Sets the component to be italic.
     *
     * @return This message component instance.
     */
    public T setItalic() {
        return setItalic(true);
    }

    /**
     * Sets the component to be italic.
     *
     * @param italic Defines if the component should or should not be italic.
     * @return This message component instance.
     */
    public T setItalic(boolean italic) {
        this.italic = italic;
        return (T) this;
    }

    /**
     * Checks if the component is underlined.
     *
     * @return true if the component is underlined, false if not.
     */
    public boolean isUnderlined() {
        return underlined != null && underlined;
    }

    /**
     * Sets the component to be underlined.
     *
     * @return This message component instance.
     */
    public T setUnderlined() {
        return setUnderlined(true);
    }

    /**
     * Sets the component to be underlined.
     *
     * @param underlined Defines if the component should or should not be underlined.
     * @return This message component instance.
     */
    public T setUnderlined(boolean underlined) {
        this.underlined = underlined;
        return (T) this;
    }

    /**
     * Checks if the component is strikethrough.
     *
     * @return true if the component is strikethrough, false if not.
     */
    public boolean isStrikethrough() {
        return strikethrough != null && strikethrough;
    }

    /**
     * Sets the component to be strikethrough.
     *
     * @return This message component instance.
     */
    public T setStrikethrough() {
        return setStrikethrough(true);
    }

    /**
     * Sets the component to be strikethrough.
     *
     * @param strikethrough Defines if the component should or should not be strikethrough.
     * @return This message component instance.
     */
    public T setStrikethrough(boolean strikethrough) {
        this.strikethrough = strikethrough;
        return (T) this;
    }

    /**
     * Checks if the component is obfuscated.
     *
     * @return true if the component is obfuscated, false if not.
     */
    public boolean isObfuscated() {
        return obfuscated != null && obfuscated;
    }

    /**
     * Sets the component to be obfuscated.
     *
     * @return This message component instance.
     */
    public T setObfuscated() {
        return setObfuscated(true);
    }

    /**
     * Sets the component to be obfuscated.
     *
     * @param obfuscated Defines if the component should or should not be obfuscated.
     * @return This message component instance.
     */
    public T setObfuscated(boolean obfuscated) {
        this.obfuscated = obfuscated;
        return (T) this;
    }

    /**
     * Gets all the extras of the component.
     *
     * @return A list of MessageComponents used as extras for the component.
     */
    public List<T> getExtras() {
        return extra;
    }

    /**
     * Sets all the extras of the component.
     *
     * @param extras A list of MessageComponents used as extras for the component.
     * @return This message component instance.
     */
    public T setExtras(List<T> extras) {
        extra = extras;
        return (T) this;
    }

    /**
     * Adds an inherited string to the component.
     *
     * @param extras The extras to be added to the component.
     * @return This message component instance.
     */
    public T addExtra(T... extras) {
        if (extras != null) {
            for (T extra : extras) {
                if (extra == null)
                    continue;
                if (this.extra == null) {
                    this.extra = new LinkedList<>();
                }
                this.extra.add(extra);
            }
        }
        return (T) this;
    }

    /**
     * Sets the formats of the component.
     *
     * @param formats The array of formats to apply to the component.
     * @return This message component instance.
     * @exception IllegalArgumentException If any of the enumeration values in the array do not represent formatters.
     */
    public T setFormats(MessageColor... formats) throws IllegalArgumentException {
        if (formats != null) {
            for (MessageColor style : formats) {
                if (!style.isFormat()) {
                    throw new IllegalArgumentException(style.name() + " is not a formatter");
                }
            }
            for (MessageColor format : formats) {
                switch (format) {
                case ITALIC:
                    setItalic();
                    break;
                case BOLD:
                    setBold();
                    break;
                case UNDERLINE:
                    setUnderlined();
                    break;
                case STRIKETHROUGH:
                    setStrikethrough();
                    break;
                case MAGIC:
                    setObfuscated();
                    break;
                }
            }
        }
        return (T) this;
    }

    /**
     * Sets the formats of the component.
     * Use {@link MessageColor} for better performance!
     *
     * @param formats The array of formats to apply to the component.
     * @return This message component instance.
     * @exception IllegalArgumentException If any of the enumeration values in the array do not represent formatters.
     */
    public T setFormats(@Nullable STYLES... formats) throws IllegalArgumentException {
        if (formats == null || formats.length == 0)
            return (T) this;
        return setFormats(MessageColor.messageColorArrayFromStylesArray((Enum[]) formats));
    }

    /**
     * Sets the style of the component.
     *
     * @param styles The array of styles to apply to the component.
     * @return This message component instance.
     */
    public T setStyles(MessageColor... styles) {
        if (styles != null) {
            for (MessageColor style : styles) {
                if (style == null)
                    continue;
                if (style.isFormat()) {
                    setFormats(style);
                }
                if (style.isColor()) {
                    setColor(style);
                }
            }
        }
        return (T) this;
    }

    /**
     * Sets the style of the component.
     * Use {@link MessageColor} for better performance!
     *
     * @param styles The array of styles to apply to the component.
     * @return This message component instance.
     */
    public T setStyles(@Nullable STYLES... styles) {
        if (styles == null || styles.length == 0)
            return (T) this;
        return setStyles(MessageColor.messageColorArrayFromStylesArray((Enum[]) styles));
    }
    //endregion

    //region Short message modifier (setter)
    /**
     * Sets the text of the component.
     *
     * @param text The new text of the component.
     * @return This message component instance.
     */
    public T text(String text) {
        return setText(text);
    }

    /**
     * Sets the color of the component.
     *
     * @param color The new color of the component.
     * @return This message component instance.
     */
    public T color(String color) {
        return setColor(color);
    }

    /**
     * Sets the color of the component.
     *
     * @param color The new color of the component.
     * @return This message component instance.
     * @exception IllegalArgumentException If the specified {@code ChatColor} enumeration value is not a color (but a format value).
     */
    public T color(MessageColor color) throws IllegalArgumentException {
        return setColor(color);
    }

    /**
     * Sets the color of the component.
     * Use {@link MessageColor} for better performance!
     *
     * @param color The new color of the component.
     * @return This message component instance.
     */
    public T color(STYLES color) throws IllegalArgumentException {
        return setColor(color);
    }

    /**
     * Sets the format of the component.
     *
     * @param formats The array of format to apply to the component.
     * @return This message component instance.
     * @exception IllegalArgumentException If any of the enumeration values in the array do not represent formatters.
     */
    public T format(MessageColor... formats) throws IllegalArgumentException {
        return setFormats(formats);
    }

    /**
     * Sets the format of the component.
     * Use {@link MessageColor} for better performance!
     *
     * @param formats The array of format to apply to the component.
     * @return This message component instance.
     * @exception IllegalArgumentException If any of the enumeration values in the array do not represent formatters.
     */
    public T format(STYLES... formats) throws IllegalArgumentException {
        return setFormats(formats);
    }

    /**
     * Sets the style of the component.
     * Use {@link MessageColor} for better performance!
     *
     * @param styles The array of styles to apply to the component.
     * @return This message component instance.
     */
    public T style(STYLES... styles) {
        return setStyles(styles);
    }

    /**
     * Sets the style of the component.
     *
     * @param styles The array of styles to apply to the component.
     * @return This message component instance.
     */
    public T style(MessageColor... styles) {
        return setStyles(styles);
    }

    /**
     * Sets the component to be bold.
     *
     * @return This message component instance.
     */
    public T bold() {
        return setBold();
    }

    /**
     * Sets the component to be italic.
     *
     * @return This message component instance.
     */
    public T italic() {
        return setItalic();
    }

    /**
     * Sets the component to be underlined.
     *
     * @return This message component instance.
     */
    public T underlined() {
        return setUnderlined();
    }

    /**
     * Sets the component to be obfuscated.
     *
     * @return This message component instance.
     */
    public T obfuscated() {
        return setObfuscated();
    }

    /**
     * Sets the component to be strikethrough.
     *
     * @return This message component instance.
     */
    public T strikethrough() {
        return setStrikethrough();
    }

    /**
     * Set the behavior of the component when the client clicks on it.
     *
     * @param action the action the client should execute.
     * @param value the value the client should use for the action.
     * @return This message component instance.
     */
    public T onClick(MessageClickEvent.ClickEventAction action, String value) {
        return setClickEvent(new MessageClickEvent(action, value));
    }

    /**
     * Set the behavior of the component to instruct the client to open a file on the client side filesystem when the component is clicked.
     *
     * @param path The path of the file on the clients filesystem.
     * @return This message component instance.
     */
    public T file(String path) {
        return onClick(MessageClickEvent.ClickEventAction.OPEN_FILE, path);
    }

    /**
     * Set the behavior of the component to instruct the client to open a web-page in the clients web browser when the component is clicked.
     *
     * @param url The URL of the page to open when the link is clicked.
     * @return This message component instance.
     */
    public T link(String url) {
        return onClick(MessageClickEvent.ClickEventAction.OPEN_URL, url);
    }

    /**
     * Set the behavior of the component to instruct the client to replace the chat input box content with the specified string when the component is clicked.
     * The client will not immediately send the command to the server to be executed unless the client player submits the command/chat message, usually with the enter key.
     *
     * @param command The text to display in the chat bar of the client.
     * @return This message component instance.
     */
    public T suggest(String command) {
        return onClick(MessageClickEvent.ClickEventAction.SUGGEST_COMMAND, command);
    }

    /**
     * Set the behavior of the component to instruct the client to send the specified string to the server as a chat message when the component is clicked.
     * The client <b>will</b> immediately send the command to the server to be executed when the editing component is clicked.
     *
     * @param command The text to display in the chat bar of the client.
     * @return This message component instance.
     */
    public T command(final String command) {
        return onClick(MessageClickEvent.ClickEventAction.RUN_COMMAND, command);
    }

    /**
     * Set the behavior of the component to instruct the client to append the chat input box content with the specified string when the component is shift-clicked.
     * The client will not immediately send the command to the server to be executed unless the client player submits the command/chat message, usually with the enter key.
     *
     * @param insert The text to append to the chat bar of the client.
     * @return This message component instance.
     */
    public T insert(String insert) {
        return setInsertion(insert);
    }

    /**
     * Set the behavior of the component when the client hovers with the mouse over it.
     *
     * @param action the action the client should execute.
     * @param value the value the client should use for the action.
     * @return This message component instance.
     */
    public T onHover(MessageHoverEvent.HoverEventAction action, String value) {
        return setHoverEvent(new MessageHoverEvent(action, value));
    }

    /**
     * Set the behavior of the component when the client hovers with the mouse over it.
     *
     * @param action the action the client should execute.
     * @param value the value the client should use for the action.
     * @return This message component instance.
     */
    public T onHover(MessageHoverEvent.HoverEventAction action, Collection<? extends MessageComponent> value) {
        return setHoverEvent(new MessageHoverEvent(action, value));
    }

    /**
     * Set the behavior of the component to display information about an achievement when the client hovers over the text.
     *
     * @param name The name of the achievement to display, excluding the "achievement." prefix.
     * @return This message component instance.
     */
    public T achievementTooltip(String name) {
        return onHover(MessageHoverEvent.HoverEventAction.SHOW_ACHIEVEMENT, "achievement." + name);
    }

    /**
     * Set the behavior of the component to display information about a statistic when the client hovers over the text.
     *
     * @param name The name of the statistic to display, excluding the "stat." prefix.
     * @return This message component instance.
     */
    public T statisticTooltip(String name) {
        return onHover(MessageHoverEvent.HoverEventAction.SHOW_ACHIEVEMENT, "stat." + name);
    }

    /**
     * Set the behavior of the component to display information about an item when the client hovers over the text.
     *
     * @param itemJSON A string representing the JSON-serialized NBT data tag of an ItemStack.
     * @return This message component instance.
     */
    public T itemTooltip(String itemJSON) {
        return onHover(MessageHoverEvent.HoverEventAction.SHOW_ITEM, itemJSON);
    }

    /**
     * Set the behavior of the component to display raw text when the client hovers over the text.
     *
     * @param lines The lines of text which will be displayed to the client upon hovering.
     * @return This message component instance.
     */
    public T tooltip(String... lines) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < lines.length; i++) {
            if (i > 0) {
                builder.append('\n');
            }
            builder.append(lines[i]);
        }
        return onHover(MessageHoverEvent.HoverEventAction.SHOW_TEXT, builder.toString());
    }

    /**
     * Set the behavior of the component to display formatted text when the client hovers over the text.
     *
     * @param text The formatted text which will be displayed to the client upon hovering.
     * @return This message component instance.
     */
    public T formattedTooltip(MessageComponent... text) throws IllegalArgumentException {
        StringBuilder builder = new StringBuilder("");
        for (MessageComponent t : text) {
            if (t.getClickEvent() != null)
                throw new IllegalArgumentException("The tooltip text cannot have click data.");
            else if (t.getHoverEvent() != null)
                throw new IllegalArgumentException("The tooltip text cannot have a tooltip.");
            builder.append(t.toString());
        }
        return onHover(MessageHoverEvent.HoverEventAction.SHOW_TEXT, builder.toString());
    }

    protected abstract MessageComponent getNewLineComponent();

    /**
     * Set the behavior of the component to display the specified lines of formatted text when the client hovers over the text.
     *
     * @param lines The lines of formatted text which will be displayed to the client upon hovering.
     * @return This message component instance.
     */
    public T formattedTooltip(Message... lines) throws IllegalArgumentException {
        if (lines.length < 1) {
            return setHoverEvent(null);
        }

        List<MessageComponent> components = new LinkedList<>();

        for (int i = 0; i < lines.length; i++) {
            for (MessageComponent component : lines[i].getMessageComponents()) {
                if (component.getClickEvent() != null)
                    throw new IllegalArgumentException("The tooltip text cannot have click data.");
                else if (component.getHoverEvent() != null)
                    throw new IllegalArgumentException("The tooltip text cannot have a tooltip.");
                if (i > 0) {
                    components.add(getNewLineComponent());
                }
                components.add(component);
            }
        }
        return onHover(MessageHoverEvent.HoverEventAction.SHOW_TEXT, components);
    }

    /**
     * Adds an inherited string to the component.
     *
     * @param extras The extras to be added to the component.
     * @return This message component instance.
     */
    public T extra(T... extras) {
        return addExtra(extras);
    }
    //endregion

    //region Deserializer and Deserializer Functions
    //region deserializer variables
    protected transient static Gson GSON;
    protected transient static Constructor messageComponentConstructor;
    protected transient static Class messageComponentClass;
    protected static final transient JsonParser JSON_PARSER = new JsonParser();
    //endregion

    @Override
    public T deserialize(JsonElement json, java.lang.reflect.Type type, JsonDeserializationContext context)
            throws JsonParseException {
        JsonObject componentAsJsonObject = json.getAsJsonObject();
        T component;
        try {
            component = (T) messageComponentConstructor.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        //Strings
        if (componentAsJsonObject.get("text") != null)
            component.text = componentAsJsonObject.get("text").getAsString();
        if (componentAsJsonObject.get("color") != null)
            component.color = MessageColor.valueOf(componentAsJsonObject.get("color").getAsString());
        if (componentAsJsonObject.get("insertion") != null)
            component.insertion = componentAsJsonObject.get("insertion").getAsString();
        //Booleans
        if (componentAsJsonObject.get("bold") != null)
            component.bold = componentAsJsonObject.get("bold").getAsBoolean();
        if (componentAsJsonObject.get("italic") != null)
            component.italic = componentAsJsonObject.get("italic").getAsBoolean();
        if (componentAsJsonObject.get("underlined") != null)
            component.underlined = componentAsJsonObject.get("underlined").getAsBoolean();
        if (componentAsJsonObject.get("obfuscated") != null)
            component.obfuscated = componentAsJsonObject.get("obfuscated").getAsBoolean();
        if (componentAsJsonObject.get("strikethrough") != null)
            component.strikethrough = componentAsJsonObject.get("strikethrough").getAsBoolean();
        //Extra List
        if (componentAsJsonObject.get("extra") != null)
            component.extra = fromJsonArrayWorker(componentAsJsonObject.get("extra").getAsJsonArray());
        //Events
        if (componentAsJsonObject.get("clickEvent") != null)
            component.clickEvent = GSON.fromJson(componentAsJsonObject.get("clickEvent"), MessageClickEvent.class);
        if (componentAsJsonObject.get("hoverEvent") == null)
            component.hoverEvent = GSON.fromJson(componentAsJsonObject.get("hoverEvent"), MessageHoverEvent.class);
        //Other stuff
        component.selector = componentAsJsonObject.get("with");
        component.selector = componentAsJsonObject.get("score");
        component.selector = componentAsJsonObject.get("selector");
        component.selector = componentAsJsonObject.get("translate");
        return component;
    }

    /**
     * Generates a MessageComponent list from a given JSON string.
     *
     * @param jsonString The JSON string representing the components.
     * @return A list of MessageComponent objects. An empty list if there are no components in the given {@link JsonArray}.
     */
    protected List<T> fromJsonWorker(String jsonString) {
        return fromJsonArrayWorker(JSON_PARSER.parse(jsonString).getAsJsonArray());
    }

    /**
     * Generates a MessageComponent list from a given {@link JsonArray} object.
     *
     * @param componentArray The {@link JsonArray} containing all the components, from the deserializer.
     * @return A list of MessageComponent objects. An empty list if there are no components in the given {@link JsonArray}.
     */
    protected List<T> fromJsonArrayWorker(JsonArray componentArray) {
        List<T> components = new LinkedList<>();
        for (JsonElement component : componentArray) {
            if (component instanceof JsonPrimitive) {
                try {
                    T messageComponent = (T) messageComponentConstructor.newInstance();
                    messageComponent.setText(component.getAsString());
                    components.add(messageComponent);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else if (component instanceof JsonObject) {
                components.add((T) GSON.fromJson(component, messageComponentClass));
            } else if (component instanceof JsonArray) {
                components.addAll(fromJsonArrayWorker((JsonArray) component));
            }
        }
        return components;
    }
    //endregion
}