Java tutorial
/* * DefaultRouter.java * * The MIT License (MIT) * * Copyright (c) 2014 Graham Howden <graham_howden1 at yahoo.co.uk>. * * 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.publicuhc.pluginframework.routing; import com.google.common.base.*; import com.google.common.collect.*; import com.google.inject.AbstractModule; import com.google.inject.Inject; import com.google.inject.Injector; import com.publicuhc.pluginframework.routing.exception.CommandInvocationException; import com.publicuhc.pluginframework.routing.exception.CommandParseException; import com.publicuhc.pluginframework.routing.functions.ApplicableRoutePredicate; import com.publicuhc.pluginframework.routing.functions.ExactRouteMatchPredicate; import com.publicuhc.pluginframework.routing.functions.SubroutePredicate; import com.publicuhc.pluginframework.routing.parser.RoutingMethodParser; import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.PluginCommand; import org.bukkit.plugin.PluginLogger; import org.bukkit.util.StringUtil; import java.lang.reflect.Method; import java.util.*; import java.util.logging.Level; public class DefaultRouter implements Router { /** * Stores the message to send a player if a route wasn't found for the given command and parameters */ protected final Multimap<String, String> noRouteMessages = ArrayListMultimap.create(); /** * Used to inject all parameters needed to the command classes when added */ private final Injector injector; /** * Used to parse the annotations into usable objects */ private final RoutingMethodParser parser; /** * Stores map of command name -> routes for invocation later on */ protected final Multimap<String, CommandRoute> commands = HashMultimap.create(); private final PluginLogger logger; @Inject protected DefaultRouter(RoutingMethodParser parser, Injector injector, PluginLogger logger) { this.injector = injector; this.parser = parser; this.logger = logger; } @Override public Object registerCommands(Class klass) throws CommandParseException { return registerCommands(klass, new ArrayList<AbstractModule>()); } @Override public Object registerCommands(Class klass, List<AbstractModule> modules) throws CommandParseException { Injector childInjector = injector.createChildInjector(modules); //grab an instance of the class after injection Object o = childInjector.getInstance(klass); //register it using the instanced version without injection registerCommands(o, false); //return the created object return o; } @Override public void registerCommands(Object object, boolean inject) throws CommandParseException { registerCommands(object, inject, new ArrayList<AbstractModule>()); } @Override public void registerCommands(Object object, boolean inject, List<AbstractModule> modules) throws CommandParseException { logger.log(Level.INFO, "Loading commands from class: " + object.getClass().getName()); //inject if we need to if (inject) { Injector childInjector = injector.createChildInjector(modules); childInjector.injectMembers(object); } //the class of our object Class klass = object.getClass(); //get all of the classes methods Method[] methods = klass.getDeclaredMethods(); for (Method method : methods) { //if it's a command method if (parser.hasCommandMethodAnnotation(method)) { //attempt to parse the route CommandRoute route = parser.parseCommandMethodAnnotation(method, object); PluginCommand command = Bukkit.getPluginCommand(route.getCommandName()); //if the command required is 'ungettable' throw an error if (null == command) throw new CommandParseException( "Cannot register the command with name " + route.getCommandName()); //set ourselves as the executor for the command command.setExecutor(this); command.setTabCompleter(this); //add to command map commands.put(route.getCommandName(), route); logger.log(Level.INFO, "Loading command '" + route.getCommandName() + "' from: " + method.getName()); } } logger.log(Level.INFO, "Loaded all commands from class: " + object.getClass().getName()); } @Override public void setDefaultMessageForCommand(String commandName, List<String> message) { noRouteMessages.replaceValues(commandName, message); } @Override public void setDefaultMessageForCommand(String commandName, String message) { //run the regular method setDefaultMessageForCommand(commandName, Lists.newArrayList(message)); } /** * Fetches the most applicable route (the routes that matches with the longest subcommand string) * * @param command the command to check for * @param args the argument list to check for subcommands from * @return the most applicable route */ private Optional<CommandRoute> getMostApplicableRoute(Command command, String[] args) { List<CommandRoute> routes = getApplicableRoutes(command, args); if (routes.size() == 0) { return Optional.absent(); } Collections.sort(routes, new SubcommandLengthComparator(true)); return Optional.of(routes.get(0)); } /** * Get a list of all of the routes for the command filtered to all routes that apply to the given args * * @param command the command to check the route of * @param args the argument list to check for subcommands from * @return collection of routes that match */ private List<CommandRoute> getApplicableRoutes(Command command, String[] args) { Collection<CommandRoute> allRoutes = commands.get(command.getName()); return Lists.newArrayList(Collections2.filter(allRoutes, new ApplicableRoutePredicate(args, false))); } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { Optional<CommandRoute> routeOptional = getMostApplicableRoute(command, args); if (routeOptional.isPresent()) { CommandRoute route = routeOptional.get(); String[] subcommandArgs = Arrays.copyOfRange(args, route.getStartsWith().length, args.length); //run the actual command try { route.run(command, sender, subcommandArgs); } catch (CommandInvocationException e) { e.printStackTrace(); } return true; } //we did't know how to handle this command //get the list of messages set as the defaults for the command Collection<String> messages = noRouteMessages.get(command.getName()); //if none are set use the one set in the plugin.yml via bukkit if (messages.isEmpty()) { return false; } //send all of the messages and return true to bukkit for (String message : messages) { sender.sendMessage(message); } return true; } /** * On tab complete. The final arg is the one we want to match against, can be empty */ @Override public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) { Preconditions.checkArgument(args.length > 0); List<String> tabCompleteList = Lists.newArrayList(); List<String> route = Lists.newArrayList(args); //get the item to tab complete and remove it from the list String partial = route.get(route.size() - 1); route = route.subList(0, route.size() - 1); //check if already have any flags int index = Iterables.indexOf(route, new Predicate<String>() { @Override public boolean apply(String input) { return input.startsWith("-"); } }); //if there are any options remove everything past the first one if (index > -1) { route = route.subList(0, index); } //get all of the routes for the command Collection<CommandRoute> allRoutes = commands.get(command.getName()); //all of the subroutes Collection<CommandRoute> subroutes = Collections2.filter(allRoutes, new SubroutePredicate(route)); //exact matches Collection<CommandRoute> exacts = Collections2.filter(allRoutes, new ExactRouteMatchPredicate(route)); for (CommandRoute r : exacts) { for (String key : r.getOptionDetails().recognizedOptions().keySet()) { if (!key.equals("[arguments]")) { tabCompleteList.add("-" + key); } } } for (CommandRoute r : subroutes) { //this should pass as the predicate makes sure there is a next arg String nextInSubroute = r.getStartsWith()[route.size()]; tabCompleteList.add(nextInSubroute); } List<String> actualComplete = Lists.newArrayList(); StringUtil.copyPartialMatches(partial, tabCompleteList, actualComplete); return actualComplete; } }