org.diorite.cfg.messages.Messages.java Source code

Java tutorial

Introduction

Here is the source code for org.diorite.cfg.messages.Messages.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.cfg.messages;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;

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

import org.diorite.cfg.messages.Message.MessageData;
import org.diorite.chat.component.BaseComponent;
import org.diorite.command.sender.CommandSender;
import org.diorite.utils.collections.maps.CaseInsensitiveMap;

/**
 * Represent messages node, every node may contains message objects and other nodes.
 */
public class Messages {
    /**
     * languages supported by this messages instance.
     */
    protected final Locale[] languages;
    /**
     * Used in get/set method to separate nodes.
     */
    protected final char nodeSeparator; // '.' by default
    private final Messages parentNode;
    private final Map<String, Message> messages;
    private final Map<String, Messages> nodes;

    private Messages(final Locale[] languages, final char nodeSeparator, final Messages parentNode,
            final Map<String, Message> messages, final Map<String, Messages> nodes) {
        this.languages = languages;
        this.nodeSeparator = nodeSeparator;
        this.messages = messages;
        this.nodes = nodes;
        this.parentNode = parentNode;
    }

    private Messages(final Locale[] languages, final char nodeSeparator, final Messages parentNode) {
        this.languages = languages;
        this.nodeSeparator = nodeSeparator;
        this.messages = new CaseInsensitiveMap<>(20, .5f);
        this.nodes = new CaseInsensitiveMap<>(5, .2f);
        this.parentNode = parentNode;
    }

    /**
     * Construct new Messages root node with given separator, messages and sub-nodes.
     *
     * @param languages     languages supported by this messages instance.
     * @param nodeSeparator node separator.
     * @param messages      messages in this node.
     * @param nodes         sub-nodes for this node.
     */
    public Messages(final Locale[] languages, final char nodeSeparator, final Map<String, Message> messages,
            final Map<String, Messages> nodes) {
        this.languages = languages;
        this.nodeSeparator = nodeSeparator;
        this.messages = new CaseInsensitiveMap<>(messages);
        this.nodes = new CaseInsensitiveMap<>(nodes);
        this.parentNode = null;
    }

    /**
     * Construct new Messages root node with given separator.
     *
     * @param languages     languages supported by this messages instance.
     * @param nodeSeparator node separator.
     */
    public Messages(final Locale[] languages, final char nodeSeparator) {
        this.languages = languages;
        this.nodeSeparator = nodeSeparator;
        this.messages = new CaseInsensitiveMap<>(20, .5f);
        this.nodes = new CaseInsensitiveMap<>(5, .2f);
        this.parentNode = null;
    }

    /**
     * Construct new Messages root node with given messages and sub-nodes.
     *
     * @param languages languages supported by this messages instance.
     * @param messages  messages in this node.
     * @param nodes     sub-nodes for this node.
     */
    public Messages(final Locale[] languages, final Map<String, Message> messages,
            final Map<String, Messages> nodes) {
        this.languages = languages;
        this.nodeSeparator = '.';
        this.messages = new CaseInsensitiveMap<>(messages);
        this.nodes = new CaseInsensitiveMap<>(nodes);
        this.parentNode = null;
    }

    /**
     * Construct new Messages root.
     *
     * @param languages languages supported by this messages instance.
     */
    public Messages(final Locale... languages) {
        this.languages = languages;
        this.nodeSeparator = '.';
        this.messages = new CaseInsensitiveMap<>(20, .5f);
        this.nodes = new CaseInsensitiveMap<>(5, .2f);
        this.parentNode = null;
    }

    /**
     * Returns node separator, '.' by default.
     *
     * @return node separator.
     */
    public char getNodeSeparator() {
        return this.nodeSeparator;
    }

    /**
     * Returns parent node if any exist.
     *
     * @return parent node or null.
     */
    public Messages getParentNode() {
        return this.parentNode;
    }

    /**
     * Add new message instance on given path.
     *
     * @param message message to add.
     * @param path    path to new messages node, you may use '.' (by default) as path node separator. {@link #getNodeSeparator()}
     */
    public void addMessage(final Message message, final String path) {
        this.addMessage(message, StringUtils.split(path, this.nodeSeparator));
    }

    /**
     * Add new message instance on given path.
     *
     * @param message message to add.
     * @param path    path to new messages node, do not use any separator like dots here.
     */
    public void addMessage(final Message message, final String... path) {
        if (path.length == 0) {
            throw new IllegalArgumentException("Added mesage must have name");
        }
        if (path.length == 1) {
            this.messages.put(path[0], message);
            return;
        }
        Messages prevNode;
        Messages node = this;
        for (int i = 0; i < (path.length - 1); i++) {
            prevNode = node;
            node = node.nodes.get(path[i]);
            if (node == null) {
                node = new MessagePack(prevNode, path[i]);
                prevNode.nodes.put(path[i], node);
            }
        }
        node.addMessage(message, new String[] { path[path.length - 1] });
    }

    /**
     * Returns message instance on given path.
     *
     * @param path path to messages node, do not use any separator like dots here.
     *
     * @return message instance on given path.
     */
    public Message getMessage(final String... path) {
        if (path.length == 0) {
            return null;
        }
        if (path.length == 1) {
            return this.messages.get(path[0]);
        }
        Messages node = this;
        for (int i = 0; i < (path.length - 1); i++) {
            node = node.nodes.get(path[i]);
            if (node == null) {
                return null;
            }
        }
        return node.messages.get(path[path.length - 1]);
    }

    /**
     * Returns message instance on given path.
     *
     * @param path path to messages node, you may use '.' (by default) as path node separator. {@link #getNodeSeparator()}
     *
     * @return message instance on given path.
     */
    public Message getMessage(final String path) {
        return this.getMessage(StringUtils.split(path, this.nodeSeparator));
    }

    /**
     * Returns node with messages on given path.
     *
     * @param path path to messages node, do not use any separator like dots here.
     *
     * @return Another {@link Messages} node if exist.
     */
    public Messages getMessages(final String... path) {
        if (path.length == 0) {
            return this;
        }
        if (path.length == 1) {
            return this.nodes.get(path[0]);
        }
        Messages node = this;
        for (final String pathNode : path) {
            node = node.nodes.get(pathNode);
            if (node == null) {
                return null;
            }
        }
        return node;
    }

    /**
     * Returns node with messages on given path.
     *
     * @param path path to messages node, you may use '.' (by default) as path node separator. {@link #getNodeSeparator()}
     *
     * @return Another {@link Messages} node if exist.
     */
    public Messages getMessages(final String path) {
        return this.getMessages(StringUtils.split(path, this.nodeSeparator));
    }

    /**
     * Try broadcast this message to selected comamnd senders in target sender language if possible, if message is disabled method will just return false.
     *
     * @param path    path to message.
     * @param targets targets of message.
     * @param data    placeholder objects to use.
     *
     * @return true if message was send.
     */
    public boolean broadcastMessage(final String path, final Iterable<? extends CommandSender> targets,
            final MessageData... data) {
        final Message message = this.getMessage(path);
        return (message != null) && message.broadcastMessage(targets, data);
    }

    /**
     * Try broadcast this message (to all players) in target player language if possible, if message is disabled method will just return false.
     *
     * @param path path to message.
     * @param data placeholder objects to use.
     *
     * @return true if message was send.
     */
    public boolean broadcastMessage(final String path, final MessageData... data) {
        final Message message = this.getMessage(path);
        return (message != null) && message.broadcastMessage(data);
    }

    /**
     * Try broadcast this message to selected comamnd senders in target sender language if possible, if message is disabled method will just return false.
     *
     * @param path    path to message.
     * @param targets targets of message.
     * @param lang    default language to use if target don't have any.
     * @param data    placeholder objects to use.
     *
     * @return true if message was send.
     */
    public boolean broadcastMessage(final String path, final Iterable<? extends CommandSender> targets,
            final Locale lang, final MessageData... data) {
        final Message message = this.getMessage(path);
        return (message != null) && message.broadcastMessage(targets, lang, data);
    }

    /**
     * Try broadcast this message (to all players) in target player language if possible, if message is disabled method will just return false.
     *
     * @param path path to message.
     * @param lang default language to use if target don't have any.
     * @param data placeholder objects to use.
     *
     * @return true if message was send.
     */
    public boolean broadcastMessage(final String path, final Locale lang, final MessageData... data) {
        final Message message = this.getMessage(path);
        return (message != null) && message.broadcastMessage(lang, data);
    }

    /**
     * Try broadcast this message to selected comamnd senders, if message is disabled method will just return false.
     *
     * @param path    path to message.
     * @param targets targets of message.
     * @param lang    language to use if possible.
     * @param data    placeholder objects to use.
     *
     * @return true if message was send.
     */
    public boolean broadcastStaticMessage(final String path, final Iterable<? extends CommandSender> targets,
            final Locale lang, final MessageData... data) {
        final Message message = this.getMessage(path);
        return (message != null) && message.broadcastStaticMessage(targets, lang, data);
    }

    /**
     * Try broadcast this message (to all players), if message is disabled method will just return false.
     *
     * @param path path to message.
     * @param lang language to use if possible.
     * @param data placeholder objects to use.
     *
     * @return true if message was send.
     */
    public boolean broadcastStaticMessage(final String path, final Locale lang, final MessageData... data) {
        final Message message = this.getMessage(path);
        return (message != null) && message.broadcastStaticMessage(lang, data);
    }

    /**
     * Try send this message to given {@link CommandSender}, if message is disabled method will just return false.
     *
     * @param path   path to message.
     * @param target target of message.
     * @param lang   language to use if possible.
     * @param data   placeholder objects to use.
     *
     * @return true if message was send.
     */
    public boolean sendMessage(final String path, final CommandSender target, final Locale lang,
            final MessageData... data) {
        final Message message = this.getMessage(path);
        return (message != null) && message.sendMessage(target, lang, data);
    }

    /**
     * Try send this message to given {@link CommandSender}, if message is disabled method will just return false.
     *
     * @param path   path to message.
     * @param target target of message.
     * @param data   placeholder objects to use.
     *
     * @return true if message was send.
     */
    public boolean sendMessage(final String path, final CommandSender target, final MessageData... data) {
        final Message message = this.getMessage(path);
        return (message != null) && message.sendMessage(target, target.getPreferredLocale(), data);
    }

    /**
     * Serialize all messages to given map, all base components are serialized to json string.
     *
     * @param map             map where all messages will be added.
     * @param defaultLanguage default locale of messages.
     *
     * @return this same map as given.
     *
     * @see BaseComponent#canBeLegacy()
     */
    @SuppressWarnings("unchecked")
    public Map<Locale, Map<String, Object>> toMap(final Map<Locale, Map<String, Object>> map,
            final Locale defaultLanguage) {
        for (final Entry<String, Message> entry : this.messages.entrySet()) {
            final String key = entry.getKey();
            final Message message = entry.getValue();

            final Map<Locale, Map<String, Object>> messageValues = message
                    .toMap(new HashMap<>(this.languages.length), defaultLanguage, key);
            for (final Entry<Locale, Map<String, Object>> localeMapEntry : messageValues.entrySet()) {
                final Locale locale = localeMapEntry.getKey();
                final Map<String, Object> valueMap = localeMapEntry.getValue();

                Map<String, Object> targetMap = map.get(locale);
                if (targetMap == null) {
                    targetMap = new CaseInsensitiveMap<>(20);
                    map.put(locale, targetMap);
                }
                targetMap.putAll(valueMap);
            }
        }
        for (final Entry<String, Messages> entry : this.nodes.entrySet()) {
            final String key = entry.getKey();
            final Messages messages = entry.getValue();

            final Map<Locale, Map<String, Object>> messageValues = messages
                    .toMap(new HashMap<>(this.languages.length), defaultLanguage);
            for (final Entry<Locale, Map<String, Object>> localeMapEntry : messageValues.entrySet()) {
                final Locale locale = localeMapEntry.getKey();
                final Map<String, Object> valueMap = localeMapEntry.getValue();

                Map<String, Object> nestedMap = map.get(locale);
                if (nestedMap == null) {
                    nestedMap = new CaseInsensitiveMap<>(20);
                    map.put(locale, nestedMap);
                }
                final Object tmp = nestedMap.get(key);
                if ((tmp != null) && !(tmp instanceof Map)) {
                    throw new RuntimeException("Duplicated node and message for key: " + key + ": " + tmp);
                }
                final Map<String, Object> targetMap;
                if (tmp == null) {
                    targetMap = new CaseInsensitiveMap<>(20);
                    nestedMap.put(key, targetMap);
                } else {
                    targetMap = (Map<String, Object>) tmp;
                }
                targetMap.putAll(valueMap);
            }
        }
        return map;
    }

    /**
     * Add given node to nodes map.
     *
     * @param pack node to add.
     */
    protected void addNode(final MessagePack pack) {
        this.nodes.put(pack.node, pack);
    }

    protected static class MessagePack extends Messages {
        private final String node;

        protected MessagePack(final Messages parentNode, final String node) {
            super(parentNode.languages, parentNode.nodeSeparator, parentNode);
            this.node = node;
        }

        protected MessagePack(final Messages parentNode, final String node, final Map<String, Message> messages,
                final Map<String, Messages> nodes) {
            super(parentNode.languages, parentNode.nodeSeparator, parentNode, messages, nodes);
            this.node = node;
        }

        @Override
        public String toString() {
            return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("node", this.node).toString();
        }
    }

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