123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- /*
- * Copyright (c) 2006-2014 DMDirc Developers
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
- package com.dmdirc.commandparser.commands.flags;
-
- import com.dmdirc.FrameContainer;
- import com.dmdirc.commandparser.CommandArguments;
-
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Map;
-
- /**
- * Utility class for commands which allow the user to specify shell-like flags (
- * <code>--foo</code>).
- *
- * @since 0.6.5
- */
- public class CommandFlagHandler {
-
- /** A map of all known flag names to their flag objects. */
- private final Map<String, CommandFlag> flags = new HashMap<>();
- /** A map of currently enabled flag names to their flag objects. */
- private final Map<String, CommandFlag> enabledFlags = new HashMap<>();
- /** A map of currently disabled flag names to their flag objects. */
- private final Map<String, CommandFlag> disabledFlags = new HashMap<>();
- /** A map of disabled flag names to the flag objects that caused them to be disabled. */
- private final Map<String, CommandFlag> disabledBy = new HashMap<>();
-
- /**
- * Creates a new command flag handler which will handle all of the specified flags.
- *
- * @param flags The flags that will be handled
- */
- public CommandFlagHandler(final CommandFlag... flags) {
- for (CommandFlag flag : flags) {
- this.flags.put(flag.getName(), flag);
- }
- }
-
- /**
- * Processes the specified arguments and parses out command flags. If the specified arguments
- * aren't valid given the flags belonging to this command flag handler, an error message is sent
- * to the origin and <code>null</code> is returned from this method.
- *
- * @param origin The container where the command was entered
- * @param arguments The arguments passed to the command
- *
- * @return A corresponding {@link CommandFlagResult} object, or null if some problem was
- * encountered.
- */
- public CommandFlagResult process(final FrameContainer origin,
- final CommandArguments arguments) {
- final Map<CommandFlag, Integer> results = parse(origin, arguments);
-
- return results == null ? null : new CommandFlagResult(arguments, results);
- }
-
- /**
- * Parses the specified arguments and returns the offsets of the arguments for each found
- * command flag.
- *
- * @param origin The container where the command was entered
- * @param arguments The arguments passed to the command
- *
- * @return A map of discovered command flags to the offset of the flag's first argument within
- * the <code>arguments</code> object. If an error occurs, null is returned.
- */
- protected Map<CommandFlag, Integer> parse(final FrameContainer origin,
- final CommandArguments arguments) {
- enabledFlags.clear();
- disabledBy.clear();
- disabledFlags.clear();
-
- for (CommandFlag flag : flags.values()) {
- (flag.isEnabled() ? enabledFlags : disabledFlags).put(flag.getName(), flag);
- }
-
- final Map<CommandFlag, Integer> results = new HashMap<>();
- final List<CommandFlag> delayedFlags = new ArrayList<>(flags.size());
-
- int offset;
- for (offset = 0; offset < arguments.getArguments().length; offset++) {
- final String arg = arguments.getArguments()[offset];
- String name;
-
- if (arg.startsWith("--")
- && flags.containsKey(name = arg.substring(2).toLowerCase())) {
- final CommandFlag flag = flags.get(name);
-
- if (enabledFlags.containsKey(name)) {
- // It's enabled!
- handleEnable(flag);
-
- // Handle any immediate arguments
- if ((offset = readArguments(flag, arguments, offset + 1,
- flag.getImmediateArgs(), origin, results)) == -1) {
- return null;
- }
-
- // Handle delayed arguments (if any)
- if (flag.getDelayedArgs() > 0) {
- delayedFlags.add(flag);
- }
- } else if (disabledBy.containsKey(name)) {
- // Disabled by another flag
- sendLine(origin, arguments.isSilent(), "commandError",
- "Cannot use flag --" + name
- + " in conjunction with --" + disabledBy.get(name).getName());
- return null;
- } else {
- // Disabled because not yet enabled
- sendLine(origin, arguments.isSilent(), "commandError",
- "Cannot use flag --" + name
- + " without " + getEnablers(flag));
- return null;
- }
- } else {
- break;
- }
- }
-
- // Handle any stored delayed arguments
- for (CommandFlag flag : delayedFlags) {
- if ((offset = readArguments(flag, arguments, offset,
- flag.getDelayedArgs(), origin, results)) == -1) {
- return null;
- }
-
- offset++;
- }
-
- results.put(null, offset);
-
- return results;
- }
-
- /**
- * Reads the arguments for the specified flag.
- *
- * @param flag The flag that is being read
- * @param arguments The raw arguments for the command
- * @param offset The index that the first argument will be at
- * @param argCount The number of arguments that need to be read
- * @param origin The source of the command (for error messages)
- * @param results The map to place results into
- *
- * @return The index of the last argument that was handled, or -1 if there were insufficient
- * arguments for the flag
- */
- protected int readArguments(final CommandFlag flag,
- final CommandArguments arguments, final int offset, final int argCount,
- final FrameContainer origin, final Map<CommandFlag, Integer> results) {
- final int lastArg = argCount + offset - 1;
-
- if (arguments.getArguments().length <= lastArg) {
- sendLine(origin, arguments.isSilent(),
- "commandError", "Flag --" + flag.getName() + " expects "
- + argCount + " argument"
- + (argCount == 1 ? "" : "s"));
- return -1;
- }
-
- results.put(flag, offset);
-
- return lastArg;
- }
-
- /**
- * Processes the enabled and disabled lists for the specified flag, and adds them to the
- * relevant properties.
- *
- * @param flag The flag whose enables/disables lists should be processed
- */
- protected void handleEnable(final CommandFlag flag) {
- for (CommandFlag target : flag.getDisables()) {
- if (enabledFlags.containsKey(target.getName())) {
- enabledFlags.remove(target.getName());
- disabledFlags.put(target.getName(), target);
- disabledBy.put(target.getName(), flag);
- }
- }
-
- for (CommandFlag target : flag.getEnables()) {
- if (disabledFlags.containsKey(target.getName())) {
- disabledFlags.remove(target.getName());
- enabledFlags.put(target.getName(), target);
- disabledBy.remove(target.getName());
- }
- }
- }
-
- /**
- * Constructs a user-friendly string describing the flag(s) which must be used in order to
- * enable the specified flag. This is useful for error messages when the user tries to use a
- * disabled flag.
- *
- * @param flag The flag to find enablers for
- *
- * @return A user-friendly string describing flags which enable the specified flag.
- */
- protected String getEnablers(final CommandFlag flag) {
- final List<CommandFlag> enablers = new LinkedList<>();
-
- for (CommandFlag target : flags.values()) {
- if (target.getEnables().contains(flag)) {
- enablers.add(target);
- }
- }
-
- if (enablers.size() == 1) {
- return "--" + enablers.get(0).getName();
- }
-
- final StringBuilder res = new StringBuilder("one of ");
- for (CommandFlag enabler : enablers) {
- res.append("--");
- res.append(enabler.getName());
- res.append(", ");
- }
-
- return res.substring(0, res.length() - 2);
- }
-
- /**
- * Convenience method to send a line to the specified frame container.
- *
- * @param origin The container to send the line to
- * @param isSilent Whether the command is silenced or not
- * @param messageType The type of the line to be sent
- * @param args The arguments for the specified messageType
- */
- protected static void sendLine(final FrameContainer origin, final boolean isSilent,
- final String messageType, final Object... args) {
- if (origin != null && !isSilent) {
- origin.addLine(messageType, args);
- }
- }
-
- }
|