/*
* Copyright (C) 2010 sk89q <http://www.sk89q.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copied and modified on March 9, 2013 by dumptruckman.
*/
package pluginbase.command;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import pluginbase.messages.Message;
import java.util.*;
/**
* Contains information regarding the current usage of a command such as what args and flags were used.
*/
public final class CommandContext {
private final String command;
private final List<String> parsedArgs;
private final List<Integer> originalArgIndices;
private final Set<Character> booleanFlags = new HashSet<Character>();
private final Map<Character, String> valueFlags = new HashMap<Character, String>();
CommandContext(@NotNull String args) throws CommandException {
this(args.split(" "), null);
}
CommandContext(@NotNull String[] args) throws CommandException {
this(args, null);
}
CommandContext(@NotNull String args, @Nullable Set<Character> valueFlags) throws CommandException {
this(args.split(" "), valueFlags);
}
/**
* @param args An array with arguments including the command. Empty strings outside quotes will be removed.
* @param valueFlags A set containing all value flags. Pass null to disable value flag parsing.
* @throws CommandException This is thrown if flag fails for some reason.
*/
CommandContext(@NotNull String[] args, @Nullable Set<Character> valueFlags) throws CommandException {
if (valueFlags == null) {
valueFlags = Collections.emptySet();
}
command = args[0];
// Eliminate empty args and combine multi-word args first
List<Integer> argIndexList = new ArrayList<Integer>(args.length);
List<String> argList = new ArrayList<String>(args.length);
for (int i = 1; i < args.length; ++i) {
String arg = args[i];
if (arg.length() == 0) {
continue;
}
argIndexList.add(i);
switch (arg.charAt(0)) {
case '\'':
case '"':
final StringBuilder build = new StringBuilder();
final char quotedChar = arg.charAt(0);
int endIndex;
for (endIndex = i; endIndex < args.length; ++endIndex) {
final String arg2 = args[endIndex];
if (arg2.charAt(arg2.length() - 1) == quotedChar && arg2.length() > 1) {
if (endIndex != i) build.append(' ');
build.append(arg2.substring(endIndex == i ? 1 : 0, arg2.length() - 1));
break;
} else if (endIndex == i) {
build.append(arg2.substring(1));
} else {
build.append(' ').append(arg2);
}
}
if (endIndex < args.length) {
arg = build.toString();
i = endIndex;
}
// In case there is an empty quoted string
if (arg.length() == 0) {
continue;
}
// else raise exception about hanging quotes?
}
argList.add(arg);
}
// Then flags
this.originalArgIndices = new ArrayList<Integer>(argIndexList.size());
this.parsedArgs = new ArrayList<String>(argList.size());
for (int nextArg = 0; nextArg < argList.size(); ) {
// Fetch argument
String arg = argList.get(nextArg++);
// Not a flag?
if (arg.charAt(0) != '-' || arg.length() == 1 || !arg.matches("^-[a-zA-Z]+$")) {
originalArgIndices.add(argIndexList.get(nextArg - 1));
parsedArgs.add(arg);
continue;
}
// Handle flag parsing terminator --
if (arg.equals("--")) {
while (nextArg < argList.size()) {
originalArgIndices.add(argIndexList.get(nextArg));
parsedArgs.add(argList.get(nextArg++));
}
break;
}
// Go through the flag characters
for (int i = 1; i < arg.length(); ++i) {
char flagName = arg.charAt(i);
if (valueFlags.contains(flagName)) {
if (this.valueFlags.containsKey(flagName)) {
throw new CommandException(CommandHandler.VALUE_FLAG_ALREADY_GIVEN.bundle(flagName));
}
if (nextArg >= argList.size()) {
throw new CommandException(CommandHandler.NO_VALUE_FOR_VALUE_FLAG.bundle(flagName));
}
// If it is a value flag, read another argument and add it
this.valueFlags.put(flagName, argList.get(nextArg++));
} else {
booleanFlags.add(flagName);
}
}
}
}
/**
* Gets the name of the command.
*
* @return the name of the command.
*/
public String getCommand() {
return command;
}
/**
* Checks whether the given command matches this command, ignoring case.
*
* @param command the command to check
* @return true if it matches the used command, ignoring case.
*/
public boolean matches(String command) {
return this.command.equalsIgnoreCase(command);
}
/**
* Gets the arg at the given index.
*
* @param index the index of the arg to retrieve.
* @return the arg at the given index.
* @throws java.lang.IndexOutOfBoundsException if the given index does not exist in the list of args.
*/
public String getString(int index) throws IndexOutOfBoundsException {
return parsedArgs.get(index);
}
/**
* Gets the arg at the given index or the given default if that index is not valid.
*
* @param index the index of the arg to retrieve.
* @param def the default to use if the index is not valid.
* @return the arg at the given index or the given default if that index is not valid.
*/
public String getString(int index, String def) {
return index < parsedArgs.size() ? parsedArgs.get(index) : def;
}
/**
* Gets the integer arg at the given index.
*
* @param index the index of the integer arg to retrieve.
* @return the integer arg at the given index.
* @throws NumberFormatException if the arg at the given index is not an integer.
* @throws java.lang.IndexOutOfBoundsException if the given index does not exist in the list of args.
*/
public Integer getInteger(int index) throws NumberFormatException, IndexOutOfBoundsException {
return Integer.parseInt(parsedArgs.get(index));
}
/**
* Gets the integer arg at the given index or the given default if that index is not valid.
*
* @param index the index of the integer arg to retrieve.
* @param def the default to use if the index is not valid.
* @return the integer arg at the given index or the given default if that index is not valid.
* @throws NumberFormatException if the arg at the given index is not an integer.
*/
public Integer getInteger(int index, int def) throws NumberFormatException {
return index < parsedArgs.size() ? Integer.parseInt(parsedArgs.get(index)) : def;
}
/**
* Gets the long arg at the given index.
*
* @param index the index of the long arg to retrieve.
* @return the long arg at the given index.
* @throws NumberFormatException if the arg at the given index is not a long.
* @throws java.lang.IndexOutOfBoundsException if the given index does not exist in the list of args.
*/
public Long getLong(int index) throws NumberFormatException, IndexOutOfBoundsException {
return Long.parseLong(parsedArgs.get(index));
}
/**
* Gets the long arg at the given index or the given default if that index is not valid.
*
* @param index the index of the long arg to retrieve.
* @param def the default to use if the index is not valid.
* @return the long arg at the given index or the given default if that index is not valid.
* @throws NumberFormatException if the arg at the given index is not a long.
*/
public Long getLong(int index, int def) throws NumberFormatException {
return index < parsedArgs.size() ? Long.parseLong(parsedArgs.get(index)) : def;
}
/**
* Gets the double arg at the given index.
*
* @param index the index of the double arg to retrieve.
* @return the double arg at the given index.
* @throws NumberFormatException if the arg at the given index is not a double.
* @throws java.lang.IndexOutOfBoundsException if the given index does not exist in the list of args.
*/
public Double getDouble(int index) throws NumberFormatException {
return Double.parseDouble(parsedArgs.get(index));
}
/**
* Gets the double arg at the given index or the given default if that index is not valid.
*
* @param index the index of the double arg to retrieve.
* @param def the default to use if the index is not valid.
* @return the double arg at the given index or the given default if that index is not valid.
* @throws NumberFormatException if the arg at the given index is not a double.
*/
public Double getDouble(int index, double def) throws NumberFormatException {
return index < parsedArgs.size() ? Double.parseDouble(parsedArgs.get(index)) : def;
}
/**
* Checks if the args include the given flag.
*
* @param flag the flag to check for.
* @return true if the flag is present in the args.
*/
public boolean hasFlag(char flag) {
return booleanFlags.contains(flag) || valueFlags.containsKey(flag);
}
/**
* Gets the set of flags used with the command.
*
* @return the set of flags used with the command.
*/
public Set<Character> getFlags() {
return booleanFlags;
}
/**
* A mapping of any value flags used and their values.
*
* @return a map with the keys as the flag used and the value as the value of those flags.
*/
public Map<Character, String> getValueFlags() {
return valueFlags;
}
/**
* Gets the value of the given value flag.
*
* @param flag the flag to get the value for.
* @return the value of the given flag or null if the flag is not present.
*/
public String getFlag(char flag) {
return valueFlags.get(flag);
}
/**
* Gets the value of the given value flag or the given default if the flag is not present.
*
* @param flag the flag to get the value for.
* @param def the default to use if the flag is not present.
* @return the value of the given flag or the given default if the flag is not present.
*/
public String getFlag(char flag, String def) {
final String value = valueFlags.get(flag);
return value != null ? value : def;
}
/**
* Gets the integer value of the given value flag.
*
* @param flag the flag to get the integer value for.
* @return the integer value of the given flag or null if the flag is not present.
* @throws NumberFormatException if the flag value is not an integer.
*/
public Integer getFlagInteger(char flag) throws NumberFormatException {
return Integer.parseInt(valueFlags.get(flag));
}
/**
* Gets the integer value of the given value flag or the given default if the flag is not present.
*
* @param flag the flag to get the integer value for.
* @param def the default to use if the flag is not present.
* @return the integer value of the given flag or the given default if the flag is not present.
* @throws NumberFormatException if the flag value is not an integer.
*/
public Integer getFlagInteger(char flag, int def) throws NumberFormatException {
final String value = valueFlags.get(flag);
return value != null ? Integer.parseInt(value) : def;
}
/**
* Gets the long value of the given value flag.
*
* @param flag the flag to get the long value for.
* @return the long value of the given flag or null if the flag is not present.
* @throws NumberFormatException if the flag value is not a long.
*/
public Long getFlagLong(char flag) throws NumberFormatException {
return Long.parseLong(valueFlags.get(flag));
}
/**
* Gets the long value of the given value flag or the given default if the flag is not present.
*
* @param flag the flag to get the long value for.
* @param def the default to use if the flag is not present.
* @return the long value of the given flag or the given default if the flag is not present.
* @throws NumberFormatException if the flag value is not a long.
*/
public Long getFlagLong(char flag, long def) throws NumberFormatException {
final String value = valueFlags.get(flag);
return value != null ? Long.parseLong(value) : def;
}
/**
* Gets the double value of the given value flag.
*
* @param flag the flag to get the double value for.
* @return the double value of the given flag or null if the flag is not present.
* @throws NumberFormatException if the flag value is not a double.
*/
public Double getFlagDouble(char flag) throws NumberFormatException {
return Double.parseDouble(valueFlags.get(flag));
}
/**
* Gets the double value of the given value flag or the given default if the flag is not present.
*
* @param flag the flag to get the double value for.
* @param def the default to use if the flag is not present.
* @return the double value of the given flag or the given default if the flag is not present.
* @throws NumberFormatException if the flag value is not a double.
*/
public Double getFlagDouble(char flag, double def) throws NumberFormatException {
final String value = valueFlags.get(flag);
return value != null ? Double.parseDouble(value) : def;
}
/**
* Gets the number of arguments used with the command not including flags.
*
* @return the number of arguments used with the command not including flags.
*/
public int argsLength() {
return parsedArgs.size();
}
}