de.doncarnage.minecraft.common.commandhandler.tree.impl.CommandTree.java Source code

Java tutorial

Introduction

Here is the source code for de.doncarnage.minecraft.common.commandhandler.tree.impl.CommandTree.java

Source

/*
 *     BasePlugin
 *   Copyright (C) 2014 Viciouss <http://www.doncarnage.de>
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Lesser 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
 *   Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
 *   USA
 */

package de.doncarnage.minecraft.common.commandhandler.tree.impl;

import de.doncarnage.minecraft.common.commandhandler.tree.Node;
import de.doncarnage.minecraft.common.commandhandler.tree.NodeException;
import de.doncarnage.minecraft.common.commandhandler.tree.NodeTree;
import de.doncarnage.minecraft.common.commandhandler.tree.PathDescriptor;
import de.doncarnage.minecraft.common.commandhandler.tree.impl.exception.ConflictingContentException;
import de.doncarnage.minecraft.common.commandhandler.tree.impl.exception.ConflictingParameterNodeException;
import de.doncarnage.minecraft.common.commandhandler.tree.impl.exception.MalformedNodePathException;
import de.doncarnage.minecraft.common.commandhandler.tree.impl.exception.NodeNotFoundException;
import de.doncarnage.minecraft.common.commandhandler.tree.impl.verificators.FixedParamVerificator;
import de.doncarnage.minecraft.common.commandhandler.tree.impl.verificators.OptionalParamVerificator;
import de.doncarnage.minecraft.common.commandhandler.tree.impl.verificators.RepeatableParamVerificator;
import de.doncarnage.minecraft.common.commandhandler.tree.impl.verificators.VariableParamVerificator;
import org.apache.commons.lang.StringUtils;

import java.util.EnumMap;
import java.util.Map;

public class CommandTree<T extends PathDescriptor> implements NodeTree<T> {

    private Node<T> rootNode;
    private Map<NodeType, ChildHandler<T>> childVerificators;

    public CommandTree() {
        rootNode = new CommandNode<>("root", null, false, false, false);
        addChildVerificators();
    }

    @Override
    public void addContent(T pathDescriptor) {
        String fullCommand = pathDescriptor.getPathString();
        String[] path = StringUtils.split(fullCommand, " ");
        validate(path);

        Node<T> current = createNodePath(path);
        if (current != null) {
            while (current.isOptional()) {
                current = current.getParent();
            }
            if (current.getContent() != null) {
                throw new ConflictingContentException(
                        String.format("There is already a node at this position: %s", fullCommand));
            }
            current.setContent(pathDescriptor);
        } else {
            throw new NodeException(String.format("Could not create path for command: %s", fullCommand));
        }
    }

    private void validate(String[] path) {
        checkForOptionalOrRepeatableNodeBeforeLastPosition(path);
        checkForOptionalNodeBeforeLastPosition(path);
    }

    private void checkForOptionalNodeBeforeLastPosition(String[] path) {
        boolean hit = false;
        for (String node : path) {
            if (hit)
                throw new MalformedNodePathException(
                        String.format("Optional nodes may not be at any other position but the last! Command: %s",
                                getPathForArray(path)));
            hit = NodeType.isOptionalNodeName(node);
        }
    }

    private void checkForOptionalOrRepeatableNodeBeforeLastPosition(String[] path) {
        boolean hit = false;
        for (String node : path) {
            if (hit)
                throw new MalformedNodePathException(
                        String.format("Repeatable nodes may not be at any other position but the last! Command: %s",
                                getPathForArray(path)));
            hit = NodeType.isRepeatableNodeName(node);
        }
    }

    private Node<T> getNodeForPath(String[] path) {
        Node<T> current = rootNode;
        for (String element : path) {
            current = current.getChildNodeByName(element);
            if (current == null)
                return null;
        }
        return current;
    }

    private Node<T> resolveNodeForPath(String[] path) {
        Node<T> current = rootNode;
        boolean isSecondOptionalNode = false;
        for (int i = 0; i < path.length; i++) {
            String element = path[i];
            current = current.getChildNodeByName(element);
            if (current == null)
                throw new NodeNotFoundException(
                        "A node with the name " + element + " does not exist. Command: " + getPathForArray(path));
            if (current.hasAtLeastOneChild() && current.getAllChildren().get(0).isOptional()
                    && !current.getAllChildren().get(0).isRepeatable() && i < path.length - 1) {
                if (isSecondOptionalNode) {
                    throw new NodeException("There are too many arguments in command: " + getPathForArray(path));
                }
                isSecondOptionalNode = true;
            }
        }
        return current;
    }

    private String getPathForArray(String[] array) {
        return StringUtils.join(array, " ");
    }

    private Node<T> getNodeForPath(String path) {
        return getNodeForPath(StringUtils.split(path, " "));
    }

    private Node<T> createNodePath(String[] path) {
        Node<T> current = rootNode;
        for (String element : path) {
            Node<T> parentNode = current;
            current = current.getChildNodeByName(element);
            if (current != null && current.isParam() && !current.getName().equals(element)) {
                throw new ConflictingParameterNodeException(
                        "You tried to add a parameter node with a different name than the one already existant. This is not allowed. Path: "
                                + getPathForArray(path));
            } else if (current == null) {
                boolean isParamNode = NodeType.isParamNodeName(element);
                boolean isRepeatableNode = NodeType.isRepeatableNodeName(element);
                boolean isOptionalNode = NodeType.isOptionalNodeName(element);
                Node<T> childNode = new CommandNode<>(element, parentNode, isParamNode, isRepeatableNode,
                        isOptionalNode);
                checkAdditionOfNode(parentNode, childNode);
                parentNode.addChild(childNode);
                current = childNode;
            }
        }
        return current;
    }

    private void checkAdditionOfNode(Node<T> parentNode, Node<T> childNode) {
        if (parentNode.isRepeatable() || parentNode.isOptional()) {
            throw new MalformedNodePathException("Repeatable and optional nodes cannot have any children!");
        }
        childVerificators.get(NodeType.getNodeTypeBySign(childNode.getName())).verifyChildAddition(parentNode);

    }

    private void addChildVerificators() {
        childVerificators = new EnumMap<>(NodeType.class);
        childVerificators.put(NodeType.KEYWORD, new FixedParamVerificator<T>());
        childVerificators.put(NodeType.NEEDED, new VariableParamVerificator<T>());
        childVerificators.put(NodeType.OPTIONAL, new OptionalParamVerificator<T>());
        ChildHandler<T> repeatableChildHandler = new RepeatableParamVerificator<>();
        childVerificators.put(NodeType.NEEDED_REPEATABLE, repeatableChildHandler);
        childVerificators.put(NodeType.OPTIONAL_REPEATABLE, repeatableChildHandler);
    }

    @Override
    public void removeContent(T pathDescriptor) {
        if (pathDescriptor == null)
            return;
        Node<T> currentNode = getNodeForPath(pathDescriptor.getPathString());
        if (currentNode == null)
            return;
        currentNode.setContent(null);

        while (currentNode.hasParent() && currentNode.getChildCount() <= 0 && currentNode.getContent() == null
                && !currentNode.equals(rootNode)) {
            Node<T> parentNode = currentNode.getParent();
            parentNode.removeChild(currentNode);
            currentNode = parentNode;
        }

    }

    public Node<T> resolveContentNode(String[] commandString) {
        return resolveNodeForPath(commandString);
    }

    @Override
    public Node<T> resolveContentNode(String commandString) {
        return resolveContentNode(StringUtils.split(commandString, " "));
    }

    @Override
    public Node<T> getLastNodeOnPath(String path) throws NodeException {
        return getLastNodeOnPath(StringUtils.split(path, " "));
    }

    public Node<T> getLastNodeOnPath(String[] commandString) throws NodeException {
        return getLastNodeForPath(commandString);
    }

    private Node<T> getLastNodeForPath(String[] commandString) {
        Node<T> current = rootNode;
        for (String element : commandString) {
            Node<T> next = current.getChildNodeByName(element);
            if (next == null)
                return current;
            current = next;
        }
        return current;
    }

}