/* * Copyright 2011 ZerothAngel <zerothangel@tyrannyofheaven.org> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.tyrannyofheaven.bukkit.util.command; import static org.tyrannyofheaven.bukkit.util.ToHStringUtils.hasText; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * Convenience class to parse a command's arguments and store the results. * * @author zerothangel */ final class ParsedArgs { private final Map<String, String> options = new HashMap<>(); private String[] rest = new String[0]; private OptionMetaData unparsedArgument = null; private boolean parsedPositional = false; private boolean parsed = false; // Return the OptionMetaData with the given name (usually a flag) private static OptionMetaData getOption(String flag, List<OptionMetaData> options) { for (OptionMetaData omd : options) { for (String name : omd.getNames()) { if (flag.equals(name)) { return omd; } } } return null; } /** * Parse command arguments according to the given CommandMetaData. * * @param cmd * @param args * @return */ public void parse(CommandMetaData cmd, String[] args) { if (cmd == null) throw new IllegalArgumentException("cmd cannot be null"); if (args == null) args = new String[0]; if (parsed) throw new IllegalStateException("parse already called"); parsed = true; int pos = 0; // Parse flags while (pos < args.length) { String arg = args[pos]; if ("--".equals(arg)) { // explicit end of flags pos++; parsedPositional = true; // no going back break; } else if (OptionMetaData.isArgument(arg) || cmd.getFlagOptions().isEmpty()) { // positional argument break; } else { List<String> flags = new LinkedList<>(); String flagArg = arg.substring(1); if (!flagArg.startsWith("-")) { // Not a long flag, break it up for (char c : flagArg.toCharArray()) { flags.add("-" + Character.toString(c)); } } else { // Use long flag as-is flags.add(arg); } for (String flag : flags) { OptionMetaData omd = getOption(flag, cmd.getFlagOptions()); if (omd == null) { // Unknown option throw new UnknownFlagException(flag); } // Special handling of Boolean and boolean if (omd.getType() == Boolean.class || omd.getType() == Boolean.TYPE) { options.put(omd.getName(), ""); // value doesn't matter, only existence } else { // Get value pos++; if (pos >= args.length) { // Premature end throw new MissingValueException(omd, flag); } options.put(omd.getName(), args[pos]); } } pos++; } } // Parse positional args for (OptionMetaData omd : cmd.getPositionalArguments()) { if (!omd.isOptional()) { if (pos >= args.length) { if (omd.isNullable()) { // NB: No exception will be thrown and rest of arguments // will be unset. Use with care. unparsedArgument = omd; break; } else { // Ran out of args throw new MissingValueException(omd); } } else { options.put(omd.getName(), args[pos++]); parsedPositional = true; } } else { if (pos >= args.length) { // No more args, this and the rest should be optional unparsedArgument = omd; break; } else { options.put(omd.getName(), args[pos++]); parsedPositional = true; } } } rest = Arrays.copyOfRange(args, pos, args.length); } /** * Retrieve associated value for an option. * * @param name the option name * @return the associated String value */ public String getOption(String name) { if (!hasText(name)) throw new IllegalArgumentException("name must have a value"); return options.get(name); } /** * Retrieve option map. * * @return the option map */ public Map<String, String> getOptions() { return options; } /** * Retrieve unparsed positional parameters. * * @return unparsed positional parameters */ public String[] getRest() { return rest; } /** * Retrieve OptionMetaData of first unparsed optional positional parameter. * * @return OptionMetaData of first unparsed optional positional parameter, or null */ public OptionMetaData getUnparsedArgument() { return unparsedArgument; } /** * Returns whether or not any positional arguments have been parsed yet. * * @return true if positional arguments have been parsed */ public boolean isParsedPositional() { return parsedPositional; } }