Newer
Older
ultramine_bukkit / src / main / java / org / bukkit / craftbukkit / help / SimpleHelpMap.java
@vlad20012 vlad20012 on 24 Feb 2017 7 KB initial
package org.bukkit.craftbukkit.help;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.MultipleCommandAlias;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.PluginIdentifiableCommand;
import org.bukkit.command.defaults.BukkitCommand;
import org.bukkit.command.defaults.VanillaCommand;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
import org.bukkit.help.GenericCommandHelpTopic;
import org.bukkit.help.HelpMap;
import org.bukkit.help.HelpTopic;
import org.bukkit.help.HelpTopicComparator;
import org.bukkit.help.HelpTopicFactory;
import org.bukkit.help.IndexHelpTopic;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

/**
 * Standard implementation of {@link HelpMap} for CraftBukkit servers.
 */
public class SimpleHelpMap implements HelpMap
{

	private HelpTopic defaultTopic;
	private final Map<String, HelpTopic> helpTopics;
	private final Map<Class, HelpTopicFactory<Command>> topicFactoryMap;
	private final CraftServer server;
	private HelpYamlReader yaml;

	@SuppressWarnings("unchecked")
	public SimpleHelpMap(CraftServer server)
	{
		this.helpTopics = new TreeMap<String, HelpTopic>(HelpTopicComparator.topicNameComparatorInstance()); // Using a TreeMap for its explicit sorting on key
		this.topicFactoryMap = new HashMap<Class, HelpTopicFactory<Command>>();
		this.server = server;
		this.yaml = new HelpYamlReader(server);

		Predicate indexFilter = Predicates.not(Predicates.instanceOf(CommandAliasHelpTopic.class));
		if(!yaml.commandTopicsInMasterIndex())
		{
			indexFilter = Predicates.and(indexFilter, Predicates.not(new IsCommandTopicPredicate()));
		}

		this.defaultTopic = new IndexHelpTopic("Index", null, null, Collections2.filter(helpTopics.values(), indexFilter), "Use /help [n] to get page n of help.");

		registerHelpTopicFactory(MultipleCommandAlias.class, new MultipleCommandAliasHelpTopicFactory());
	}

	public synchronized HelpTopic getHelpTopic(String topicName)
	{
		if(topicName.equals(""))
		{
			return defaultTopic;
		}

		if(helpTopics.containsKey(topicName))
		{
			return helpTopics.get(topicName);
		}

		return null;
	}

	public Collection<HelpTopic> getHelpTopics()
	{
		return helpTopics.values();
	}

	public synchronized void addTopic(HelpTopic topic)
	{
		// Existing topics take priority
		if(!helpTopics.containsKey(topic.getName()))
		{
			helpTopics.put(topic.getName(), topic);
		}
	}

	public synchronized void clear()
	{
		helpTopics.clear();
	}

	public List<String> getIgnoredPlugins()
	{
		return yaml.getIgnoredPlugins();
	}

	/**
	 * Reads the general topics from help.yml and adds them to the help index.
	 */
	public synchronized void initializeGeneralTopics()
	{
		yaml = new HelpYamlReader(server);

		// Initialize general help topics from the help.yml file
		for(HelpTopic topic : yaml.getGeneralTopics())
		{
			addTopic(topic);
		}

		// Initialize index help topics from the help.yml file
		for(HelpTopic topic : yaml.getIndexTopics())
		{
			if(topic.getName().equals("Default"))
			{
				defaultTopic = topic;
			}
			else
			{
				addTopic(topic);
			}
		}
	}

	/**
	 * Processes all the commands registered in the server and creates help topics for them.
	 */
	public synchronized void initializeCommands()
	{
		// ** Load topics from highest to lowest priority order **
		Set<String> ignoredPlugins = new HashSet<String>(yaml.getIgnoredPlugins());

		// Don't load any automatic help topics if All is ignored
		if(ignoredPlugins.contains("All"))
		{
			return;
		}

		// Initialize help topics from the server's command map
		outer:
		for(Command command : server.getCommandMap().getCommands())
		{
			if(commandInIgnoredPlugin(command, ignoredPlugins))
			{
				continue;
			}

			// Register a topic
			for(Class c : topicFactoryMap.keySet())
			{
				if(c.isAssignableFrom(command.getClass()))
				{
					HelpTopic t = topicFactoryMap.get(c).createTopic(command);
					if(t != null) addTopic(t);
					continue outer;
				}
				if(command instanceof PluginCommand && c.isAssignableFrom(((PluginCommand) command).getExecutor().getClass()))
				{
					HelpTopic t = topicFactoryMap.get(c).createTopic(command);
					if(t != null) addTopic(t);
					continue outer;
				}
			}
			addTopic(new GenericCommandHelpTopic(command));
		}

		// Initialize command alias help topics
		for(Command command : server.getCommandMap().getCommands())
		{
			if(commandInIgnoredPlugin(command, ignoredPlugins))
			{
				continue;
			}
			for(String alias : command.getAliases())
			{
				// Only register if this command owns the alias
				if(server.getCommandMap().getCommand(alias) == command)
				{
					addTopic(new CommandAliasHelpTopic("/" + alias, "/" + command.getLabel(), this));
				}
			}
		}

		// Add alias sub-index
		Collection<HelpTopic> filteredTopics = Collections2.filter(helpTopics.values(), Predicates.instanceOf(CommandAliasHelpTopic.class));
		if(!filteredTopics.isEmpty())
		{
			addTopic(new IndexHelpTopic("Aliases", "Lists command aliases", null, filteredTopics));
		}

		// Initialize plugin-level sub-topics
		Map<String, Set<HelpTopic>> pluginIndexes = new HashMap<String, Set<HelpTopic>>();
		fillPluginIndexes(pluginIndexes, server.getCommandMap().getCommands());

		for(Map.Entry<String, Set<HelpTopic>> entry : pluginIndexes.entrySet())
		{
			addTopic(new IndexHelpTopic(entry.getKey(), "All commands for " + entry.getKey(), null, entry.getValue(), "Below is a list of all " + entry.getKey() + " commands:"));
		}

		// Amend help topics from the help.yml file
		for(HelpTopicAmendment amendment : yaml.getTopicAmendments())
		{
			if(helpTopics.containsKey(amendment.getTopicName()))
			{
				helpTopics.get(amendment.getTopicName()).amendTopic(amendment.getShortText(), amendment.getFullText());
				if(amendment.getPermission() != null)
				{
					helpTopics.get(amendment.getTopicName()).amendCanSee(amendment.getPermission());
				}
			}
		}
	}

	private void fillPluginIndexes(Map<String, Set<HelpTopic>> pluginIndexes, Collection<? extends Command> commands)
	{
		for(Command command : commands)
		{
			String pluginName = getCommandPluginName(command);
			if(pluginName != null)
			{
				HelpTopic topic = getHelpTopic("/" + command.getLabel());
				if(topic != null)
				{
					if(!pluginIndexes.containsKey(pluginName))
					{
						pluginIndexes.put(pluginName, new TreeSet<HelpTopic>(HelpTopicComparator.helpTopicComparatorInstance())); //keep things in topic order
					}
					pluginIndexes.get(pluginName).add(topic);
				}
			}
		}
	}

	private String getCommandPluginName(Command command)
	{
		if(command instanceof VanillaCommandWrapper)
		{
			return "Minecraft";
		}
		if(command instanceof BukkitCommand || command instanceof VanillaCommand)
		{
			return "Bukkit";
		}
		if(command instanceof PluginIdentifiableCommand)
		{
			return ((PluginIdentifiableCommand) command).getPlugin().getName();
		}
		return null;
	}

	private boolean commandInIgnoredPlugin(Command command, Set<String> ignoredPlugins)
	{
		if((command instanceof BukkitCommand || command instanceof VanillaCommand) && ignoredPlugins.contains("Bukkit"))
		{
			return true;
		}
		if(command instanceof PluginIdentifiableCommand && ignoredPlugins.contains(((PluginIdentifiableCommand) command).getPlugin().getName()))
		{
			return true;
		}
		return false;
	}

	public void registerHelpTopicFactory(Class commandClass, HelpTopicFactory factory)
	{
		if(!Command.class.isAssignableFrom(commandClass) && !CommandExecutor.class.isAssignableFrom(commandClass))
		{
			throw new IllegalArgumentException("commandClass must implement either Command or CommandExecutor!");
		}
		topicFactoryMap.put(commandClass, factory);
	}

	private class IsCommandTopicPredicate implements Predicate<HelpTopic>
	{

		public boolean apply(HelpTopic topic)
		{
			return topic.getName().charAt(0) == '/';
		}
	}
}