Java tutorial
/* * 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; } }