Package org.mctourney.autoreferee.util.commands

Source Code of org.mctourney.autoreferee.util.commands.CommandManager$CommandDelegator

package org.mctourney.autoreferee.util.commands;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.UnrecognizedOptionException;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.text.StrMatcher;
import org.apache.commons.lang.text.StrTokenizer;

import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import org.bukkit.help.HelpTopic;
import org.bukkit.help.IndexHelpTopic;
import org.bukkit.plugin.java.JavaPlugin;

import org.mctourney.autoreferee.AutoRefMatch;
import org.mctourney.autoreferee.AutoReferee;
import org.mctourney.autoreferee.AutoRefMatch.Role;
import org.mctourney.autoreferee.util.commands.CommandManager.AutoRefCommandHelpTopic;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

public class CommandManager implements CommandExecutor, TabCompleter
{
  Map<String, HandlerNode> cmap = Maps.newHashMap();

  protected class CommandDelegator
  {
    public Object handler;
    public Method method;

    Options commandOptions;
    AutoRefCommandHelpTopic helpTopic = null;

    public CommandDelegator(Object handler, Method method, PluginCommand command)
    {
      this.method = method; this.handler = handler;

      AutoRefCommand cAnnotation = method.getAnnotation(AutoRefCommand.class);
      AutoRefPermission pAnnotation = method.getAnnotation(AutoRefPermission.class);

      if (!cAnnotation.description().isEmpty())
        helpTopic = new AutoRefCommandHelpTopic(cAnnotation, pAnnotation);

      commandOptions = new Options();
      char options[] = cAnnotation.options().toCharArray();

      for (int i = 0; i < options.length; )
      {
        char arg = options[i++];
        if (i < options.length)
        {
          if (options[i] == '*') { OptionBuilder.hasOptionalArg(); ++i; }
          else if (options[i] == '+') { OptionBuilder.hasArg(); ++i; }
        }

        commandOptions.addOption(OptionBuilder.create(arg));
      }
    }

    public boolean execute(CommandSender sender, AutoRefMatch match, String[] args)
      throws CommandPermissionException, UnrecognizedOptionException
    {
      CommandLine cli = null;

      try { cli = new GnuParser().parse(commandOptions, args); args = cli.getArgs(); }
      catch (UnrecognizedOptionException e) { throw e; }
      catch (ParseException e) { e.printStackTrace(); }

      AutoRefCommand command = method.getAnnotation(AutoRefCommand.class);
      AutoRefPermission permissions = method.getAnnotation(AutoRefPermission.class);

      // perform the args cut at this point
      args = (String[]) ArrayUtils.subarray(args, command.name().length - 1, args.length);

      // check command permissions
      checkPermissions(command, permissions, sender);

      // if the number of arguments is incorrect, just return false
      if (command.argmin() > args.length || command.argmax() < args.length) return false;

      try { return ((Boolean) method.invoke(handler, sender, match, args, cli)).booleanValue(); }
      catch (Exception e) { e.printStackTrace(); return false; }
    }
  }

  public class AutoRefCommandHelpTopic extends HelpTopic
  {
    private AutoRefPermission permissions;
    private AutoRefCommand command;
    private boolean alias = false;

    public AutoRefCommandHelpTopic(AutoRefCommand command, AutoRefPermission permissions)
    {
      this.command = command;
      this.permissions = permissions;

      this.name = "/" + StringUtils.join(command.name(), " ");
      this.shortText = command.description();
      this.setupFullText();
    }

    protected void setupFullText()
    {
      String usage = command.usage();
      if ("".equals(usage)) usage = command.argmax() == 0
        ? "<command>" : "<command> [args?]";

      this.fullText = command.description() + "\n"
        + "Usage: " + usage.replace("<command>", this.name);

      String[] opts = command.opthelp();
      if (opts.length > 0)
      {
        String opt = "Options:";
        for (int i = 0; i < opts.length; i += 2) opt += String.format(
          "\n%s   -%s, %s", ChatColor.RESET.toString(), opts[i], opts[i+1]);
        this.fullText += "\n" + opt;
      }
    }

    @Override
    public boolean canSee(CommandSender sender)
    {
      try { checkPermissions(this.command, this.permissions, sender); return true; }
      catch (CommandPermissionException e) { return false; }
    }

    public boolean isAlias()
    { return alias; }

    public AutoRefCommandHelpTopic copyAlias(String alias)
    {
      AutoRefCommandHelpTopic topic = new AutoRefCommandHelpTopic(this.command, this.permissions);

      // modify the command to insert the Bukkit alias
      String[] cmd = command.name().clone(); cmd[0] = alias;
      topic.name = "/" + StringUtils.join(cmd, " ");

      topic.alias = true;
      topic.setupFullText();
      return topic;
    }
  }

  public void checkPermissions(AutoRefCommand command, AutoRefPermission permissions, CommandSender sender)
    throws CommandPermissionException
  {
    if (sender instanceof ConsoleCommandSender && permissions != null && !permissions.console())
      throw new CommandPermissionException(command, "Command not available from console");

    if (sender instanceof Player)
    {
      Player player = (Player) sender;
      AutoRefMatch match = AutoReferee.getInstance().getMatch(player.getWorld());
      Role role = match == null ? AutoRefMatch.Role.NONE : match.getRole(player);

      if (!role.atLeast(permissions.role()))
        throw new CommandPermissionException(command, match == null
          ? "Command available only within an AutoReferee match"
          : ("Command not available to " + role.toString().toLowerCase()));

      for (String node : permissions.nodes()) if (!player.hasPermission(node))
        throw new CommandPermissionException(command, "Insufficient permissions");
    }
  }

  public void registerCommands(Object commands, JavaPlugin plugin)
  {
    for (Method method : commands.getClass().getDeclaredMethods())
    {
      AutoRefCommand command = method.getAnnotation(AutoRefCommand.class);
      if (command == null || command.name().length == 0) continue;

      PluginCommand pcommand = plugin.getCommand(command.name()[0]);
      if (pcommand == null) throw new CommandRegistrationException(method, "Command not provided in plugin.yml");

      if (method.getReturnType() != boolean.class)
        throw new CommandRegistrationException(method, "Command method must return type boolean");

      if (pcommand.getExecutor() != this) pcommand.setExecutor(this);
      if (pcommand.getTabCompleter() != this) pcommand.setTabCompleter(this);

      CommandDelegator delegator = new CommandDelegator(commands, method, pcommand);
      this.setHandler(delegator, command.name());
    }
  }

  public void generateHelp(JavaPlugin plugin)
  {
    List<HelpTopic> topics = Lists.newArrayList();
    for (Map.Entry<String, HandlerNode> e : cmap.entrySet())
    {
      PluginCommand pcommand = Bukkit.getPluginCommand(e.getKey());
      if (pcommand != null && plugin.equals(pcommand.getPlugin()))
      {
        HandlerNode handler = e.getValue();
        if (handler != null) topics.addAll(handler.getHelpTopics(pcommand));
      }
    }

    Iterator<HelpTopic> iter = topics.iterator();
    for (iter = topics.iterator(); iter.hasNext(); )
    {
      HelpTopic topic = iter.next();
      Bukkit.getHelpMap().addTopic(topic);

      // remove the aliases from the list before we populate the index
      if (topic instanceof AutoRefCommandHelpTopic &&
        ((AutoRefCommandHelpTopic) topic).isAlias()) iter.remove();
    }

    Collections.sort(topics, new Comparator<HelpTopic>()
    {
      @Override
      public int compare(HelpTopic a, HelpTopic b)
      { return a.getName().compareToIgnoreCase(b.getName()); }
    });

    String stext = String.format("Below is a list of all %s commands:", plugin.getName());
    Bukkit.getHelpMap().addTopic(new IndexHelpTopic(plugin.getName(), stext, "", topics));
  }

  @Override
  public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
  {
    AutoReferee plugin = AutoReferee.getInstance();

    World world = plugin.getSenderWorld(sender);
    AutoRefMatch match = plugin.getMatch(world);

    // reparse the args properly using the string tokenizer from org.apache.commons
    args = new StrTokenizer(StringUtils.join(args, ' '), StrMatcher.splitMatcher(),
      StrMatcher.quoteMatcher()).setTrimmerMatcher(StrMatcher.trimMatcher()).getTokenArray();

    try
    {
      HandlerNode node = cmap.get(command.getName());
      List<String> ccmd = Lists.newArrayList(command.getName());

      if (node == null) return false;

      // attempt to narrow down the method using the args
      for (String arg : args)
      {
        // lowercase to maintain case insensitivity
        arg = arg.toLowerCase();

        HandlerNode next = node.cmap.get(arg);
        if (next == null) break;

        // move on to the next node, increment the cut length
        ccmd.add(arg); node = next;
      }

      String partialcmd = "/" + StringUtils.join(ccmd, " ");
      if (node.handler != null)
      {
        // attempt to execute the handler, if false, print usage
        if (node.handler.execute(sender, match, args)) return true;
        AutoRefCommand cAnnotation = node.handler.method.getAnnotation(AutoRefCommand.class);

        String usage = cAnnotation.usage();
        if ("".equals(usage)) usage = cAnnotation.argmax() == 0
          ? "<command>" : "<command> [args?]";

        // show the usage string with the partial command
        sender.sendMessage(ChatColor.DARK_RED + usage.replace("<command>", partialcmd));
        return true;
      }

      // show possible branches in the command tree
      String options = node.cmap.size() < 5 ? StringUtils.join(node.cmap.keySet(), '|') : "[args]";
      sender.sendMessage(ChatColor.DARK_RED + partialcmd + " " + options);

      return true;
    }
    catch (CommandPermissionException e)
    { sender.sendMessage(ChatColor.DARK_GRAY + e.getMessage()); return true; }
    catch (UnrecognizedOptionException e)
    { sender.sendMessage(ChatColor.DARK_GRAY + e.getMessage()); return true; }
  }

  @Override
  public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args)
  {
    try
    {
      HandlerNode node = cmap.get(command.getName());
      if (node == null) return null;

      // attempt to narrow down the method using the args
      for (int i = 0; i < args.length - 1; ++i)
      {
        // lowercase to maintain case insensitivity
        String arg = args[i].toLowerCase();

        HandlerNode next = node.cmap.get(arg);
        if (next == null) return null;

        // move on to the next node, increment the cut length
        node = next;
      }

      if (node.handler != null) return null;
      String partial = args[args.length - 1].toLowerCase();

      // show possible branches in the command tree
      List<String> opts = Lists.newArrayList();
      for (String c : node.cmap.keySet())
        if (c.toLowerCase().startsWith(partial)) opts.add(c);
      return opts;
    }
    catch (CommandPermissionException e)
    { sender.sendMessage(ChatColor.DARK_GRAY + e.getMessage()); }

    return null;
  }

  private void setHandler(CommandDelegator handler, String[] cmd)
  {
    Map<String, HandlerNode> curr = cmap;
    HandlerNode node = null;

    // traverse handler tree, creating nodes if necessary
    for (String c : cmd)
    {
      // lowercase to maintain case insensitivity
      c = c.toLowerCase();

      if ((node = curr.get(c)) == null)
        curr.put(c, node = new HandlerNode(null));
      curr = node.cmap;
    }

    // set the appropriate handler at this node
    node.handler = handler;
  }
}

class HandlerNode
{
  protected Map<String, HandlerNode> cmap;
  protected CommandManager.CommandDelegator handler;

  public HandlerNode(CommandManager.CommandDelegator handler)
  {
    this.handler = handler;
    this.cmap = Maps.newHashMap();
  }

  protected List<AutoRefCommandHelpTopic> getHelpTopics(PluginCommand pcmd)
  {
    List<AutoRefCommandHelpTopic> topics = Lists.newArrayList();
    if (handler != null && handler.helpTopic != null)
    {
      for (String alias : pcmd.getAliases())
        topics.add(handler.helpTopic.copyAlias(alias));
      topics.add(handler.helpTopic);
    }

    for (HandlerNode child : cmap.values())
      topics.addAll(child.getHelpTopics(pcmd));
    return topics;
  }
}
TOP

Related Classes of org.mctourney.autoreferee.util.commands.CommandManager$CommandDelegator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.