org.diorite.chat.component.BaseComponent.java Source code

Java tutorial

Introduction

Here is the source code for org.diorite.chat.component.BaseComponent.java

Source

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016. Diorite (by Bartomiej Mazur (aka GotoFinal))
 *
 * 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 org.diorite.chat.component;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

import org.diorite.chat.ChatColor;

/**
 * Base class for all chat component API elements.
 */
public abstract class BaseComponent extends ReplacableComponent {
    /**
     * parent node of this node.
     */
    protected BaseComponent parent;
    /**
     * color of component, var may be null.
     */
    protected ChatColor color;
    /**
     * if component use bold style, may be null.
     */
    protected Boolean bold;
    /**
     * if component use italic style, may be null.
     */
    protected Boolean italic;
    /**
     * if component use underlined style, may be null.
     */
    protected Boolean underlined;
    /**
     * if component use strikethrough style, may be null.
     */
    protected Boolean strikethrough;
    /**
     * if component use obfuscated style, may be null.
     */
    protected Boolean obfuscated;
    /**
     * extra/next elements appended after this element.
     */
    protected List<BaseComponent> extra;
    /**
     * Click event for this component, may be null.
     */
    protected ClickEvent clickEvent;
    /**
     * Hover event for this component, may be null.
     */
    protected HoverEvent hoverEvent;

    /**
     * Construct new BaseComponent as copy of old one.
     *
     * @param old component to copy.
     */
    BaseComponent(final BaseComponent old) {
        this.color = old.getColorRaw();
        this.bold = old.isBoldRaw();
        this.italic = old.isItalicRaw();
        this.underlined = old.isUnderlinedRaw();
        this.strikethrough = old.isStrikethroughRaw();
        this.obfuscated = old.isObfuscatedRaw();
        this.clickEvent = (old.clickEvent == null) ? null : old.clickEvent.duplicate();
        this.hoverEvent = (old.hoverEvent == null) ? null : old.hoverEvent.duplicate();
        if (old.extra != null) {
            this.extra = new ArrayList<>(old.extra.size());
            this.extra.addAll(old.extra.stream().map(BaseComponent::duplicate).collect(Collectors.toList()));
        }
    }

    /**
     * Construct new empty base component.
     */
    public BaseComponent() {
    }

    /**
     * Returns true if this base component can be changed to legacy string without removing any features. <br>
     * Method just check if this component use any events that can't be used in legacy chat format.
     *
     * @return true if this base component can be changed to legacy string without removing any features.
     */
    public boolean canBeLegacy() {
        if (this.clickEvent != null) {
            return false;
        }
        if (this.hoverEvent != null) {
            return false;
        }
        final List<BaseComponent> extra = this.extra;
        if ((extra == null) || extra.isEmpty()) {
            return true;
        }
        for (final BaseComponent component : extra) {
            if (!component.canBeLegacy()) {
                return false;
            }
        }
        return true;
    }

    @Override
    protected int replace_(final String text, final BaseComponent component, int limit) {
        if (this.extra != null) {
            for (final BaseComponent bs : this.extra) {
                limit = bs.replace_(text, component, limit);
                if (limit == 0) {
                    return 0;
                }
            }
        }
        if (this.hoverEvent != null) {
            limit = this.hoverEvent.replace_(text, component, limit);
            if (limit == 0) {
                return 0;
            }
        }
        if (this.clickEvent != null) {
            limit = this.clickEvent.replace_(text, component, limit);
            if (limit == 0) {
                return 0;
            }
        }
        return limit;
    }

    @Override
    protected int replace_(final String text, final String repl, int limit) {
        if (this.extra != null) {
            for (final BaseComponent bs : this.extra) {
                limit = bs.replace_(text, repl, limit);
                if (limit == 0) {
                    return 0;
                }
            }
        }
        if (this.hoverEvent != null) {
            limit = this.hoverEvent.replace_(text, repl, limit);
            if (limit == 0) {
                return 0;
            }
        }
        if (this.clickEvent != null) {
            limit = this.clickEvent.replace_(text, repl, limit);
            if (limit == 0) {
                return 0;
            }
        }
        return limit;
    }

    /**
     * Returns parent node for this chat node, may be null.
     *
     * @return parent node for this chat node, may be null.
     */
    public BaseComponent getParent() {
        return this.parent;
    }

    /**
     * Set parent node for this chat node, may be null.
     *
     * @param parent new parent node for this chat node, may be null.
     */
    public void setParent(final BaseComponent parent) {
        this.parent = parent;
    }

    /**
     * Returns list of extra/next elements appended after this one.
     *
     * @return list of extra/next elements appended after this one.
     */
    public List<BaseComponent> getExtra() {
        return this.extra;
    }

    /**
     * Set list of extra/next elements appended after this one.
     *
     * @param components new list of extra/next elements.
     */
    public void setExtra(final List<BaseComponent> components) {
        for (final BaseComponent component : components) {
            component.parent = this;
        }
        this.extra = new ArrayList<>(components);
    }

    /**
     * Returns click event for this node, may be null.
     *
     * @return click event for this node, may be null.
     */
    public ClickEvent getClickEvent() {
        return this.clickEvent;
    }

    /**
     * Set click event for this node, may be null.
     *
     * @param clickEvent click event for this node, may be null.
     */
    public void setClickEvent(final ClickEvent clickEvent) {
        this.clickEvent = clickEvent;
    }

    /**
     * Returns hover event for this node, may be null.
     *
     * @return hover event for this node, may be null.
     */
    public HoverEvent getHoverEvent() {
        return this.hoverEvent;
    }

    /**
     * Set hover event for this node, may be null.
     *
     * @param hoverEvent new hover event for this node, may be null.
     */
    public void setHoverEvent(final HoverEvent hoverEvent) {
        this.hoverEvent = hoverEvent;
    }

    /**
     * Returns color of this node, if color of this node is null it will return color of parent node,
     * if it is also null it will return default, {@link ChatColor#WHITE} color.
     *
     * @return color of this node.
     */
    public ChatColor getColor() {
        if (this.color == null) {
            if (this.parent == null) {
                return ChatColor.WHITE;
            }
            return this.parent.getColor();
        }
        return this.color;
    }

    /**
     * Set color of this node.
     *
     * @param color new color of this node.
     */
    public void setColor(final ChatColor color) {
        this.color = color;
    }

    /**
     * Returns raw color of this node, may be null. <br>
     * Even if this method return null node can still be colored as it may inherit color from parent.
     *
     * @return color of this node, may be null.
     *
     * @see #getColor()
     */
    public ChatColor getColorRaw() {
        return this.color;
    }

    /**
     * Check if this node use bold style, it will check value of this node and parent nodes.
     *
     * @return true if this node use bold style.
     */
    public boolean isBold() {
        if (this.bold == null) {
            return (this.parent != null) && (this.parent.isBold());
        }
        return this.bold;
    }

    /**
     * Set bold flag of this node, may be null.
     *
     * @param bold new bold flag of this node, may be null.
     */
    public void setBold(final Boolean bold) {
        this.bold = bold;
    }

    /**
     * Returns bold flag of this node, may be null. <br>
     * Even if this method return null node can still be bold as it may inherit style from parent.
     *
     * @return bold flag of this node, may be null.
     */
    public Boolean isBoldRaw() {
        return this.bold;
    }

    /**
     * Check if this node use italic style, it will check value of this node and parent nodes.
     *
     * @return true if this node use italic style.
     */
    public boolean isItalic() {
        if (this.italic == null) {
            return (this.parent != null) && (this.parent.isItalic());
        }
        return this.italic;
    }

    /**
     * Set italic flag of this node, may be null.
     *
     * @param italic new italic flag of this node, may be null.
     */
    public void setItalic(final Boolean italic) {
        this.italic = italic;
    }

    /**
     * Returns italic flag of this node, may be null. <br>
     * Even if this method return null node can still be italic as it may inherit style from parent.
     *
     * @return italic flag of this node, may be null.
     */
    public Boolean isItalicRaw() {
        return this.italic;
    }

    /**
     * Check if this node use underlined style, it will check value of this node and parent nodes.
     *
     * @return true if this node use underlined style.
     */
    public boolean isUnderlined() {
        if (this.underlined == null) {
            return (this.parent != null) && (this.parent.isUnderlined());
        }
        return this.underlined;
    }

    /**
     * Set underlined flag of this node, may be null.
     *
     * @param underlined new underlined flag of this node, may be null.
     */
    public void setUnderlined(final Boolean underlined) {
        this.underlined = underlined;
    }

    /**
     * Returns underlined flag of this node, may be null. <br>
     * Even if this method return null node can still be underlined as it may inherit style from parent.
     *
     * @return underlined flag of this node, may be null.
     */
    public Boolean isUnderlinedRaw() {
        return this.underlined;
    }

    /**
     * Check if this node use strikethrough style, it will check value of this node and parent nodes.
     *
     * @return true if this node use strikethrough style.
     */
    public boolean isStrikethrough() {
        if (this.strikethrough == null) {
            return (this.parent != null) && (this.parent.isStrikethrough());
        }
        return this.strikethrough;
    }

    /**
     * Set strikethrough flag of this node, may be null.
     *
     * @param strikethrough new strikethrough flag of this node, may be null.
     */
    public void setStrikethrough(final Boolean strikethrough) {
        this.strikethrough = strikethrough;
    }

    /**
     * Returns strikethrough flag of this node, may be null. <br>
     * Even if this method return null node can still be strikethrough as it may inherit style from parent.
     *
     * @return strikethrough flag of this node, may be null.
     */
    public Boolean isStrikethroughRaw() {
        return this.strikethrough;
    }

    /**
     * Check if this node use obfuscated style, it will check value of this node and parent nodes.
     *
     * @return true if this node use obfuscated style.
     */
    public boolean isObfuscated() {
        if (this.obfuscated == null) {
            return (this.parent != null) && (this.parent.isObfuscated());
        }
        return this.obfuscated;
    }

    /**
     * Set obfuscated flag of this node, may be null.
     *
     * @param obfuscated new obfuscated flag of this node, may be null.
     */
    public void setObfuscated(final Boolean obfuscated) {
        this.obfuscated = obfuscated;
    }

    /**
     * Returns obfuscated flag of this node, may be null. <br>
     * Even if this method return null node can still be obfuscated as it may inherit style from parent.
     *
     * @return obfuscated flag of this node, may be null.
     */
    public Boolean isObfuscatedRaw() {
        return this.obfuscated;
    }

    /**
     * Add new {@link TextComponent} to this node.
     *
     * @param text new string to add.
     */
    public void addExtra(final String text) {
        this.addExtra(new TextComponent(text));
    }

    /**
     * Add new {@link BaseComponent} to this node.
     *
     * @param component new component to add.
     */
    public void addExtra(final BaseComponent component) {
        if (this.extra == null) {
            this.extra = new ArrayList<>(5);
        }
        component.parent = this;
        this.extra.add(component);
    }

    /**
     * Returns true if this node have any formatting settings.
     *
     * @return true if this node have any formatting settings.
     */
    public boolean hasFormatting() {
        return (this.color != null) || (this.bold != null) || (this.italic != null) || (this.underlined != null)
                || (this.strikethrough != null) || (this.obfuscated != null) || (this.hoverEvent != null)
                || (this.clickEvent != null);
    }

    /**
     * Returns string contains this component as plain text. (without style)
     *
     * @return string contains this component as plain text. (without style)
     */
    public String toPlainText() {
        final StringBuilder builder = new StringBuilder();
        this.toPlainText(builder);
        return builder.toString();
    }

    void toPlainText(final StringBuilder builder) {
        if (this.extra != null) {
            for (final BaseComponent e : this.extra) {
                e.toPlainText(builder);
            }
        }
    }

    /**
     * Returns string contains this component as legacy text. (without events etc, but with colors)
     *
     * @return string contains this component as legacy text. (without events etc, but with colors)
     */
    public String toLegacyText() {
        final StringBuilder builder = new StringBuilder();
        this.toLegacyText(builder);
        return builder.toString();
    }

    void toLegacyText(final StringBuilder builder) {
        if (this.extra != null) {
            for (final BaseComponent e : this.extra) {
                e.toLegacyText(builder);
            }
        }
    }

    /**
     * Adds color codes of this element to given string builder.
     *
     * @param builder string builder to be used.
     */
    protected void addFormat(final StringBuilder builder) {
        final ChatColor color = this.color;
        if (color != null) {
            builder.append(color);
        }
        if (this.isBold()) {
            builder.append(ChatColor.BOLD);
        }
        if (this.isItalic()) {
            builder.append(ChatColor.ITALIC);
        }
        if (this.isUnderlined()) {
            builder.append(ChatColor.UNDERLINE);
        }
        if (this.isStrikethrough()) {
            builder.append(ChatColor.STRIKETHROUGH);
        }
        if (this.isObfuscated()) {
            builder.append(ChatColor.MAGIC);
        }
    }

    @Override
    public abstract BaseComponent duplicate();

    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).appendSuper(super.toString())
                .append("parent", this.parent).append("color", this.color).append("bold", this.bold)
                .append("italic", this.italic).append("underlined", this.underlined)
                .append("strikethrough", this.strikethrough).append("obfuscated", this.obfuscated)
                .append("extra", this.extra).append("clickEvent", this.clickEvent)
                .append("hoverEvent", this.hoverEvent).toString();
    }

    /**
     * Returns string contains given components as legacy text. (without events etc, but with colors)
     *
     * @param components components to convert to legacy text.
     *
     * @return string contains given components as legacy text. (without events etc, but with colors)
     */
    public static String toLegacyText(final BaseComponent... components) {
        final StringBuilder builder = new StringBuilder();
        for (final BaseComponent msg : components) {
            builder.append(msg.toLegacyText());
        }
        return builder.toString();
    }

    /**
     * Returns string contains given components as plain text. (without style)
     *
     * @param components components to convert to plain text.
     *
     * @return string contains given components as plain text. (without style)
     */
    public static String toPlainText(final BaseComponent... components) {
        final StringBuilder builder = new StringBuilder();
        for (final BaseComponent msg : components) {
            builder.append(msg.toPlainText());
        }
        return builder.toString();
    }
}