/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.royaldev.royalcommands.rcommands;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.royaldev.royalcommands.RUtils;
import org.royaldev.royalcommands.RoyalCommands;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public abstract class TabCommand extends CACommand implements TabCompleter {
private final List<Short> completionTypes = new ArrayList<>();
private CompletionType alwaysUse = null;
protected TabCommand(final RoyalCommands instance, final String name, final boolean checkPermissions, final Short[] cts) {
super(instance, name, checkPermissions);
for (Short s : cts) {
if (s == null) s = 0;
this.completionTypes.add(s);
}
}
/**
* An array of all Enum values used by {@link org.royaldev.royalcommands.rcommands.TabCommand.CompletionType#ENUM}.
*
* @param cs CommandSender completing
* @param cmd Command being completed
* @param label Label of command being completed
* @param args All arguments
* @param arg Argument to complete
* @return Array of Enums to be checked
*/
@SuppressWarnings("UnusedParameters")
protected Enum[] customEnum(final CommandSender cs, final Command cmd, final String label, final String[] args, final String arg) {
return new Enum[0];
}
/**
* A list of values used by {@link org.royaldev.royalcommands.rcommands.TabCommand.CompletionType#LIST}.
*
* @param cs CommandSender completing
* @param cmd Command being completed
* @param label Label of command being completed
* @param args All arguments
* @param arg Argument to complete
* @return List of values to be checked
*/
@SuppressWarnings("UnusedParameters")
protected List<String> customList(final CommandSender cs, final Command cmd, final String label, final String[] args, final String arg) {
return new ArrayList<>();
}
/**
* Filters all completions just before they are returned. This should be overridden by any class extending
* TabCommand if necessary.
*
* @param completions Completions before filtering
* @param cs CommandSender completing
* @param cmd Command being completed
* @param label Label of command being completed
* @param args All arguments
* @param arg Argument to complete
* @return Filtered completions to return
*/
@SuppressWarnings("UnusedParameters")
protected List<String> filterCompletions(final List<String> completions, final CommandSender cs, final Command cmd, final String label, final String[] args, final String arg) {
return completions;
}
protected List<String> getCompletionsFor(final CommandSender cs, final Command cmd, final String label, final String[] args, final CompletionType ct) {
final List<String> possibilities = new ArrayList<>();
if (args.length < 1) return possibilities;
final String arg = args[args.length - 1].toLowerCase();
switch (ct) {
case ONLINE_PLAYER:
for (final Player p : this.plugin.getServer().getOnlinePlayers()) {
final String name = p.getName();
final String lowerCaseName = name.toLowerCase();
if (!lowerCaseName.startsWith(arg)) continue;
possibilities.add(lowerCaseName.equals(arg) ? 0 : possibilities.size(), name);
}
break;
case ITEM_ALIAS:
possibilities.addAll(RoyalCommands.inm.getPossibleNames(arg));
break;
case ITEM:
for (final Material m : Material.values()) {
final String name = m.name();
final String lowerCaseName = name.toLowerCase();
if (!lowerCaseName.startsWith(arg)) continue;
possibilities.add(lowerCaseName.equals(arg) ? 0 : possibilities.size(), name);
}
break;
case CUSTOM:
possibilities.addAll(this.getCustomCompletions(cs, cmd, label, args, arg));
break;
case PLUGIN:
possibilities.addAll(this.getPluginCompletions(arg));
break;
case WORLD:
for (final World w : this.plugin.getServer().getWorlds()) {
final String name = w.getName();
final String lowerCaseName = name.toLowerCase();
if (!lowerCaseName.startsWith(arg)) continue;
possibilities.add(lowerCaseName.equals(arg) ? 0 : possibilities.size(), name);
}
break;
case LIST:
final List<String> custom = this.customList(cs, cmd, label, args, arg);
if (custom == null) break;
for (final String s : custom) {
final String lowerCaseName = s.toLowerCase();
if (!lowerCaseName.startsWith(arg)) continue;
possibilities.add(lowerCaseName.equals(arg) ? 0 : possibilities.size(), s);
}
break;
case ENUM:
final Enum[] enums = this.customEnum(cs, cmd, label, args, arg);
if (enums == null) break;
for (final Enum e : enums) {
final String name = e.name();
final String lowerCaseName = name.toLowerCase();
if (!lowerCaseName.startsWith(arg)) continue;
possibilities.add(lowerCaseName.equals(arg) ? 0 : possibilities.size(), name);
}
break;
case ROYALCOMMANDS_COMMAND:
case ANY_COMMAND:
final SimpleCommandMap commandMap;
try {
final Object result = RUtils.getPrivateField(this.plugin.getServer().getPluginManager(), "commandMap");
commandMap = (SimpleCommandMap) result;
} catch (Exception e) {
break;
}
for (final Command c : commandMap.getCommands()) {
if (ct == CompletionType.ROYALCOMMANDS_COMMAND && (!(c instanceof PluginCommand) || !this.plugin.getClass().getName().equals(((PluginCommand) c).getPlugin().getClass().getName()))) {
continue;
}
final String name = c.getName();
if (possibilities.contains(name)) continue;
final String lowerCaseName = name.toLowerCase();
if (!lowerCaseName.startsWith(arg)) continue;
possibilities.add(lowerCaseName.equals(arg) ? 0 : possibilities.size(), name);
for (final String alias : c.getAliases()) {
if (possibilities.contains(alias)) continue;
final String lowerCaseAlias = alias.toLowerCase();
if (!lowerCaseAlias.startsWith(arg)) continue;
possibilities.add(lowerCaseAlias.equals(arg) ? 0 : possibilities.size(), alias);
}
}
break;
}
return this.filterCompletions(possibilities, cs, cmd, label, args, arg);
}
/**
* Gets the custom completions for an argument. This should be overridden by any class extending TabCommand. This
* will be called on any argument with a CompletionType of CUSTOM.
*
* @param cs CommandSender completing
* @param cmd Command being completed
* @param label Label of command being completed
* @param args All arguments
* @param arg Argument to complete
* @return List of possible completions (not null)
*/
@SuppressWarnings("UnusedParameters")
protected List<String> getCustomCompletions(final CommandSender cs, final Command cmd, final String label, final String[] args, final String arg) {
return new ArrayList<>();
}
protected List<String> getPluginCompletions(final String arg) {
final List<String> possibilities = new ArrayList<>();
for (final Plugin p : this.plugin.getServer().getPluginManager().getPlugins()) {
final String name = p.getName();
final String lowerCaseName = name.toLowerCase();
if (!lowerCaseName.startsWith(arg)) continue;
possibilities.add(lowerCaseName.equals(arg) ? 0 : possibilities.size(), name);
}
return possibilities;
}
@Override
public List<String> onTabComplete(final CommandSender cs, final Command cmd, final String label, final String[] args) {
try {
final ArrayList<String> possibilities = new ArrayList<>();
final String[] eargs = this.getCommandArguments(args).getExtraParameters();
final int lastArgIndex = eargs.length - 1;
final List<CompletionType> cts;
if (lastArgIndex < 0 || lastArgIndex >= this.completionTypes.size()) {
if (this.alwaysUse == null) return possibilities;
else cts = Arrays.asList(this.alwaysUse);
} else cts = CompletionType.getCompletionTypes(this.completionTypes.get(lastArgIndex));
for (final CompletionType ct : cts) {
possibilities.addAll(this.getCompletionsFor(cs, cmd, label, eargs, ct));
}
return possibilities;
} catch (final Throwable t) {
this.handleException(cs, cmd, label, args, t, "An exception occurred while tab-completing that command.");
return null;
}
}
protected void setAlwaysUse(final CompletionType alwaysUse) {
this.alwaysUse = alwaysUse;
}
protected enum CompletionType {
/**
* Completes for any online player.
*/
ONLINE_PLAYER((short) 1),
/**
* Completes for any item alias in items.csv.
*/
ITEM_ALIAS((short) 2),
/**
* Completes for any material name (see {@link Material}).
*/
ITEM((short) 4),
/**
* Completes based on
* {@link #getCustomCompletions(org.bukkit.command.CommandSender, org.bukkit.command.Command, String, String[], String)},
* which can be overridden.
*/
CUSTOM((short) 8),
/**
* Completes for any plugin loaded.
*/
PLUGIN((short) 16),
/**
* Completes for any world loaded.
*/
WORLD((short) 32),
/**
* Completes for a list specified by
* {@link #customList(org.bukkit.command.CommandSender, org.bukkit.command.Command, String, String[], String)}.
*/
LIST((short) 64),
/**
* Completes for an enum specified by
* {@link #customEnum(org.bukkit.command.CommandSender, org.bukkit.command.Command, String, String[], String)}.
*/
ENUM((short) 128),
/**
* Completes for any RoyalCommands command.
*/
ROYALCOMMANDS_COMMAND((short) 256),
/**
* Completes for any command.
*/
ANY_COMMAND((short) 512);
private final short s;
CompletionType(final short s) {
this.s = s;
}
public static List<CompletionType> getCompletionTypes(final int i) {
final List<CompletionType> cts = new ArrayList<>();
for (final CompletionType ct : CompletionType.values()) {
if ((i & ct.getShort()) <= 0) continue;
cts.add(ct);
}
return cts;
}
public short getShort() {
return this.s;
}
}
}