package io.dico.dicore.command; import io.dico.dicore.command.chat.ChatControllers; import io.dico.dicore.command.chat.IChatController; import io.dico.dicore.command.predef.DefaultGroupCommand; import io.dico.dicore.command.predef.HelpCommand; import io.dico.dicore.command.predef.PredefinedCommand; import java.util.*; public abstract class ModifiableCommandAddress implements ICommandAddress { Map children; Collection childrenMainKeys = Collections.emptyList(); // the chat controller as configured by the programmer IChatController chatController; // cache for the algorithm that finds the first chat controller going up the tree transient IChatController chatControllerCache; ModifiableCommandAddress helpChild; public ModifiableCommandAddress() { this.children = new LinkedHashMap<>(4); } @Override public boolean hasParent() { return getParent() != null; } @Override public boolean hasCommand() { return getCommand() != null; } @Override public boolean hasUserDeclaredCommand() { Command command = getCommand(); return command != null && !(command instanceof PredefinedCommand) && !(command instanceof DefaultGroupCommand); } @Override public Command getCommand() { return null; } @Override public boolean isRoot() { return false; } @Override public List getNames() { return null; } @Override public List getAliases() { List names = getNames(); if (names == null) { return null; } if (names.isEmpty()) { return Collections.emptyList(); } return names.subList(1, names.size()); } @Override public String getMainKey() { return null; } public void setCommand(Command command) { throw new UnsupportedOperationException(); } @Override public abstract ModifiableCommandAddress getParent(); @Override public RootCommandAddress getRoot() { ModifiableCommandAddress out = this; while (out.hasParent()) { out = out.getParent(); } return out.isRoot() ? (RootCommandAddress) out : null; } @Override public int getDepth() { int depth = 0; ICommandAddress address = this; while (address.hasParent()) { address = address.getParent(); depth++; } return depth; } @Override public boolean isDepthLargerThan(int value) { int depth = 0; ICommandAddress address = this; do { if (depth > value) { return true; } address = address.getParent(); depth++; } while (address != null); return false; } @Override public boolean hasChildren() { return !children.isEmpty(); } @Override public int getNumberOfRealChildren() { return childrenMainKeys.size(); } @Override public Collection getChildrenMainKeys() { return Collections.unmodifiableCollection(childrenMainKeys); } @Override public Map getChildren() { return Collections.unmodifiableMap(children); } @Override public ChildCommandAddress getChild(String key) { return children.get(key); } @Override public ChildCommandAddress getChild(String key, ExecutionContext context) throws CommandException { return getChild(key); } public void addChild(ICommandAddress child) { if (!(child instanceof ChildCommandAddress)) { throw new IllegalArgumentException("Argument must be a ChildCommandAddress"); } ChildCommandAddress mChild = (ChildCommandAddress) child; if (mChild.parent != null) { throw new IllegalArgumentException("Argument already has a parent"); } if (mChild.names.isEmpty()) { throw new IllegalArgumentException("Argument must have names"); } Iterator names = mChild.modifiableNamesIterator(); String mainKey = names.next(); if (!childrenMainKeys.contains(mainKey)) { if (!(childrenMainKeys instanceof ArrayList)) { childrenMainKeys = new ArrayList<>(); } childrenMainKeys.add(mainKey); } children.put(mainKey, mChild); while (names.hasNext()) { String name = names.next(); if (children.putIfAbsent(name, mChild) != null) { names.remove(); } } mChild.setParent(this); if (mChild.hasCommand() && mChild.getCommand() instanceof HelpCommand) { helpChild = mChild; } } public void removeChildren(boolean removeAliases, String... keys) { if (keys.length == 0) { throw new IllegalArgumentException("keys is empty"); } for (String key : keys) { ChildCommandAddress keyTarget = getChild(key); if (keyTarget == null) { continue; } if (removeAliases) { Iterator iterator = keyTarget.namesModifiable.iterator(); boolean first = true; while (iterator.hasNext()) { String alias = iterator.next(); ChildCommandAddress aliasTarget = getChild(key); if (aliasTarget == keyTarget) { if (first) { childrenMainKeys.remove(alias); } children.remove(alias); } iterator.remove(); first = false; } } else { if (key.equals(keyTarget.getMainKey())) { childrenMainKeys.remove(key); } children.remove(key); keyTarget.namesModifiable.remove(key); } } } public boolean hasHelpCommand() { return helpChild != null; } public ModifiableCommandAddress getHelpCommand() { return helpChild; } @Override public IChatController getChatController() { if (chatControllerCache == null) { if (chatController != null) { chatControllerCache = chatController; } else if (!hasParent()) { chatControllerCache = ChatControllers.defaultChat(); } else { chatControllerCache = getParent().getChatController(); } } return chatControllerCache; } public void setChatController(IChatController chatController) { this.chatController = chatController; resetChatControllerCache(new HashSet<>()); } void resetChatControllerCache(Set dejaVu) { if (dejaVu.add(this)) { chatControllerCache = chatController; for (ChildCommandAddress address : children.values()) { if (address.chatController == null) { address.resetChatControllerCache(dejaVu); } } } } @Override public ICommandDispatcher getDispatcherForTree() { return getRoot(); } void appendDebugInformation(StringBuilder target, String linePrefix, Set seen) { target.append('\n').append(linePrefix); if (!seen.add(this)) { target.append(""); return; } if (this instanceof ChildCommandAddress) { List namesModifiable = ((ChildCommandAddress) this).namesModifiable; if (namesModifiable.isEmpty()) { target.append(""); } else { Iterator keys = namesModifiable.iterator(); target.append(keys.next()).append(' '); if (keys.hasNext()) { target.append('(').append(keys.next()); while (keys.hasNext()) { target.append(" ,").append(keys.next()); } target.append(") "); } } } else { target.append(" "); } String commandClass = hasCommand() ? getCommand().getClass().getCanonicalName() : ""; target.append(commandClass); for (ChildCommandAddress child : new HashSet<>(children.values())) { child.appendDebugInformation(target, linePrefix + " ", seen); } } }