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

Java tutorial

Introduction

Here is the source code for org.diorite.chat.component.TranslatableComponent.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.MissingResourceException;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

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

/**
 * Represents text components containing translatable string, so every client can see it in own language. (string must be supported by client)
 */
public class TranslatableComponent extends BaseComponent {
    private static final ResourceBundle locales = ResourceBundle.getBundle("mojang-translations/en_US");
    private static final Pattern format = Pattern.compile("%(?:(\\d+)\\$)?([A-Za-z%]|$)");
    /**
     * String key for this message.
     */
    protected String translate;
    /**
     * Arguments of this message.
     */
    protected List<BaseComponent> with;

    /**
     * Construct new TranslatableComponent as copy of old one.
     *
     * @param original component to copy.
     */
    public TranslatableComponent(final TranslatableComponent original) {
        super(original);
        this.translate = original.getTranslate();
        this.with.addAll(original.getWith().stream().map(BaseComponent::duplicate).collect(Collectors.toList()));
    }

    /**
     * Construct new TranslatableComponent for given key and arguments.
     *
     * @param translate key for this message.
     * @param with      arguments for this message.
     */
    public TranslatableComponent(final String translate, final Object... with) {
        this.translate = translate;
        final List<BaseComponent> temp = new ArrayList<>(with.length);
        for (final Object w : with) {
            if ((w instanceof String)) {
                temp.add(new TextComponent((String) w));
            } else {
                temp.add((BaseComponent) w);
            }
        }
        this.setWith(temp);
    }

    /**
     * Construct new empty TranslatableComponent.
     */
    public TranslatableComponent() {
    }

    @Override
    public int replace_(final String text, final BaseComponent component, int limit) {
        final int startIndex = this.translate.indexOf(text);
        if (startIndex != -1) {
            final int endIndex = startIndex + text.length();
            this.translate = this.translate.substring(0, startIndex) + component.toLegacyText()
                    + this.translate.substring(endIndex);
            if (--limit == 0) {
                return 0;
            }
        }
        if (this.with != null) {
            for (final BaseComponent w : this.with) {
                limit = w.replace_(text, component, limit);
                if (limit == 0) {
                    return 0;
                }
            }
        }
        return super.replace_(text, component, limit);
    }

    @Override
    public int replace_(final String text, final String repl, int limit) {
        final int startIndex = this.translate.indexOf(text);
        if (startIndex != -1) {
            final int endIndex = startIndex + text.length();
            this.translate = this.translate.substring(0, startIndex) + repl + this.translate.substring(endIndex);
            if (--limit == 0) {
                return 0;
            }
        }
        if (this.with != null) {
            for (final BaseComponent w : this.with) {
                limit = w.replace_(text, repl, limit);
                if (limit == 0) {
                    return 0;
                }
            }
        }
        return super.replace_(text, repl, limit);
    }

    /**
     * Returns locales used to represent this component as legacy/plain text.
     *
     * @return locales used to represent this component as legacy/plain text.
     */
    public ResourceBundle getLocales() {
        return locales;
    }

    /**
     * Returns Reg-ex pattern for keys.
     *
     * @return Reg-ex pattern for keys.
     */
    public Pattern getFormat() {
        return format;
    }

    /**
     * Returns string key for this message.
     *
     * @return string key for this message.
     */
    public String getTranslate() {
        return this.translate;
    }

    /**
     * Set string key for this message.
     *
     * @param translate new string key for this message.
     */
    public void setTranslate(final String translate) {
        this.translate = translate;
    }

    /**
     * Returns arguments of this message.
     *
     * @return arguments of this message.
     */
    public List<BaseComponent> getWith() {
        return this.with;
    }

    /**
     * Set arguments of this message.
     *
     * @param components new arguments of this message.
     */
    public void setWith(final List<BaseComponent> components) {
        for (final BaseComponent component : components) {
            component.setParent(this);
        }
        this.with = components;
    }

    /**
     * Add new string argument for this message.
     *
     * @param text new string argument.
     */
    public void addWith(final String text) {
        this.addWith(new TextComponent(text));
    }

    /**
     * Add new {@link BaseComponent} argument for this message.
     *
     * @param component new argument.
     */
    public void addWith(final BaseComponent component) {
        if (this.with == null) {
            this.with = new ArrayList<>(4);
        }
        component.setParent(this);
        this.with.add(component);
    }

    @Override
    protected void toPlainText(final StringBuilder builder) {
        try {
            final String trans = locales.getString(this.translate);
            final Matcher matcher = format.matcher(trans);
            int position = 0;
            int i = 0;
            while (matcher.find(position)) {
                final int pos = matcher.start();
                if (pos != position) {
                    builder.append(trans.substring(position, pos));
                }
                position = matcher.end();

                final String formatCode = matcher.group(2);
                switch (formatCode.charAt(0)) {
                case 'd':
                case 's':
                    final String withIndex = matcher.group(1);
                    this.with.get((withIndex != null) ? (Integer.parseInt(withIndex) - 1) : i++)
                            .toPlainText(builder);
                    break;
                case '%':
                    builder.append('%');
                default:
                    break;
                }
            }
            if (trans.length() != position) {
                builder.append(trans.substring(position, trans.length()));
            }
        } catch (final MissingResourceException e) {
            builder.append(this.translate);
        }
        super.toPlainText(builder);
    }

    @Override
    protected void toLegacyText(final StringBuilder builder) {
        try {
            final String trans = locales.getString(this.translate);
            final Matcher matcher = format.matcher(trans);
            int position = 0;
            int i = 0;
            while (matcher.find(position)) {
                final int pos = matcher.start();
                if (pos != position) {
                    this.addFormat(builder);
                    builder.append(trans.substring(position, pos));
                }
                position = matcher.end();

                final String formatCode = matcher.group(2);
                switch (formatCode.charAt(0)) {
                case 'd':
                case 's':
                    final String withIndex = matcher.group(1);
                    this.with.get((withIndex != null) ? (Integer.parseInt(withIndex) - 1) : i++)
                            .toLegacyText(builder);
                    break;
                case '%':
                    this.addFormat(builder);
                    builder.append('%');
                default:
                    break;
                }
            }
            if (trans.length() != position) {
                this.addFormat(builder);
                builder.append(trans.substring(position, trans.length()));
            }
        } catch (final MissingResourceException e) {
            this.addFormat(builder);
            builder.append(this.translate);
        }
        super.toLegacyText(builder);
    }

    @Override
    public BaseComponent duplicate() {
        return new TranslatableComponent(this);
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).appendSuper(super.toString())
                .append("locales", locales).append("format", format).append("translate", this.translate)
                .append("with", this.with).toString();
    }
}