Java tutorial
/* * ExperienceMod - Bukkit server plugin for modifying the experience system in Minecraft. * Copyright (C) 2012 Kristian S. Stangeland * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ package com.comphenix.xp; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Random; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.entity.Player; import com.comphenix.xp.expressions.NamedParameter; import com.comphenix.xp.messages.*; import com.comphenix.xp.parser.Utility; import com.comphenix.xp.rewards.*; public class Action { public static final Action Default = new Action(); private double inheritMultiplier; private boolean inherit; private int id; private List<Message> messages; private Map<String, MessagedResource> rewards; private Debugger debugger; public Action() { // Default constructor rewards = new LinkedHashMap<String, MessagedResource>(); ; inheritMultiplier = 1; } public Action(String rewardType, ResourceFactory reward) { this(); addReward(rewardType, reward); } private Action(List<Message> messages, Map<String, MessagedResource> rewards, Debugger debugger, int id) { this.messages = messages; this.rewards = rewards; this.debugger = debugger; this.id = id; } /** * Adds the given reward to the action that will be triggered. * @param rewardType - name of the reward. * @param factory - factory that generates the rewards when they are needed. */ public void addReward(String rewardType, ResourceFactory factory) { getEntry(rewardType, true).setResourceFactory(factory); } /** * Set (or remove, using NULL) a list of messages that will be sent when the reward is successful. * @param rewardType - name of the reward. * @param message - messages to send. */ public void addMessage(String rewardType, List<Message> messages) { getEntry(rewardType, true).setMessages(messages); } /** * Creates the underlying message-reward entry, if it doesn't already exist. * @param rewardType - name of the reward. */ private MessagedResource getEntry(String rewardType, boolean createNew) { String enumedReward = Utility.getEnumName(rewardType); MessagedResource resource = rewards.get(enumedReward); if (createNew && resource == null) { resource = new MessagedResource(); rewards.put(enumedReward, resource); } return resource; } /** * Remove a reward by name. * @param rewardType - name of the reward to remove. */ public void removeReward(String rewardType) { rewards.remove(Utility.getEnumName(rewardType)); } /** * Retrieves a associated reward by name. * @param name - name of the reward to retrieve. * @return Factory that generates rewards of this type. */ public ResourceFactory getReward(String name) { MessagedResource resource = getEntry(name, false); return resource != null ? resource.getResourceFactory() : null; } /** * Retrieves a associated reward by type. * @param type - type of the reward to retrieve. * @return Factory that generates rewards of this type. */ public ResourceFactory getReward(RewardTypes type) { return getReward(type.name()); } /** * Retrieves a list of the name of every reward. * @return Names of every reward. */ public Collection<String> getRewardNames() { return rewards.keySet(); } /** * Removes all associated rewards. */ public void removeAll() { rewards.clear(); } /** * Determines if the given action has any rewards or messages. * @return */ public boolean hasNothing(ChannelProvider provider) { return rewards.isEmpty() && (messages == null || getChannels(provider, messages) == null); } /** * Generates a list of resources, in the same order as each associated reward factory. * @param params - parameters to use when calculating the reward. * @param provider - provider of reward services. * @param rnd - random number generator. * @return A list of resources in a specific order. */ public List<ResourceHolder> generateRewards(Collection<NamedParameter> params, RewardProvider provider, Random rnd) { // Reward the player or anyone once return generateRewards(params, provider, rnd, 1); } /** * Generates a list of resources, in the same order as each associated reward factory. * @param params - parameters to use when calculating the reward. * @param provider - provider of reward services. * @param rnd - random number generator. * @param count - number of times to reward this action. * @return A list of resources in a specific order. */ public List<ResourceHolder> generateRewards(Collection<NamedParameter> params, RewardProvider provider, Random rnd, int count) { List<ResourceHolder> result = new ArrayList<ResourceHolder>(rewards.size()); // Save some time if (count == 0) { return result; } // Generate every reward in "insertion" order for (MessagedResource factory : rewards.values()) { ResourceFactory generator = factory.getResourceFactory(); result.add(generator != null ? generator.getResource(params, rnd, count) : null); } return result; } /** * Determines whether or not a player can be rewarded (or penalized) with the given list of rewards. * @param provider - reward provider. * @param player - the player to test. * @param generatedRewards - the list of rewards to use. * @return TRUE if the action can be rewarded with this list, FALSE otherwise. */ public boolean canRewardPlayer(RewardProvider provider, Player player, List<ResourceHolder> generatedRewards) { // This is why the order is important int index = 0; // Enumerate the list of rewards for (String key : rewards.keySet()) { // Quit if we've exhausted the list if (index >= generatedRewards.size()) break; RewardService manager = provider.getByName(key); ResourceHolder resource = generatedRewards.get(index++); // See if the manager allows this if (manager != null && resource != null) { if (!manager.canReward(player, resource)) { return false; } } } // Yes we can return true; } /** * Rewards or penalizes a player with the given amount of resources. * <p> * In the resulting list the resources will be arbitrarily ordered, and resources of the same type * will be combined into one. * * @param provider - reward provider that determines specifically how to reward players. * @param player - the player to reward. * @param generatedRewards - the list of rewards to use. * @return Combined amount of resources given. */ public Collection<ResourceHolder> rewardPlayer(RewardProvider provider, Player player, List<ResourceHolder> generatedRewards) { Map<String, ResourceHolder> result = new HashMap<String, ResourceHolder>(); // Like above int index = 0; // Give every reward for (String key : rewards.keySet()) { // Quit if we've exhausted the list if (index >= generatedRewards.size()) break; RewardService manager = provider.getByName(key); ResourceHolder resource = generatedRewards.get(index++); if (manager != null && resource != null) { manager.reward(player, resource); addResource(result, resource); } } return result.values(); } /** * Rewards or penalizes a given player with resources at a given location. * <p> * In the resulting list the resources will be arbitrarily ordered, and resources of the same type * will be combined into one. * * @param provider - reward provider that determines specifically how to reward players. * @param player - the player to reward. * @param generatedRewards - the list of rewards to use. * @param point - the location to place the reward, if relevant. * @return Combined amount of resources given. */ public Collection<ResourceHolder> rewardPlayer(RewardProvider provider, Player player, List<ResourceHolder> generatedRewards, Location point) { Map<String, ResourceHolder> result = new HashMap<String, ResourceHolder>(); int index = 0; // Give every reward for (String key : rewards.keySet()) { // Quit if we've exhausted the list if (index >= generatedRewards.size()) break; RewardService manager = provider.getByName(key); ResourceHolder resource = generatedRewards.get(index++); if (manager != null && resource != null) { manager.reward(player, point, resource); addResource(result, resource); } } return result.values(); } /** * Spawns resources at the given location. * * In the resulting list the resources will be arbitrarily ordered, and resources of the same type * will be combined into one. * * @param provider - reward provider that determines specifically how to award resources. * @param world - the world where the resources should be spawned. * @param generatedRewards - the list of rewards to use. * @param point - the location to place the reward. * @return Combined amount of resources given. */ public Collection<ResourceHolder> rewardAnyone(RewardProvider provider, World world, List<ResourceHolder> generatedRewards, Location point) { Map<String, ResourceHolder> result = new HashMap<String, ResourceHolder>(); int index = 0; // Give every reward for (String key : rewards.keySet()) { // Quit if we've exhausted the list if (index >= generatedRewards.size()) break; RewardService manager = provider.getByName(key); ResourceHolder resource = generatedRewards.get(index++); if (manager != null && resource != null) { manager.reward(world, point, resource); addResource(result, resource); } } return result.values(); } private void addResource(Map<String, ResourceHolder> result, ResourceHolder resource) { if (result.containsKey(resource.getName())) { // Add the previous value resource = result.get(resource.getName()).add(resource); } // Save value result.put(resource.getName(), resource); } /** * Sends a general message informing anyone listening of the resources awarded. * @param provider - channel provider to use. * @param formatter - message formatter, complete with all the parameter information. */ public void announceMessages(ChannelProvider provider, MessageFormatter formatter) { // Emote from no one emoteMessages(provider, formatter, null); } /** * Sends a message from the given player informing anyone listening of * the action performed and the resources awarded. * @param provider - channel provider to use. * @param formatter - message formatter, complete with all the parameter information. * @param player - the sender. */ public void emoteMessages(ChannelProvider provider, MessageFormatter formatter, Player player) { List<ResourceHolder> generated = formatter.getGenerated(); Iterator<String> rewardKeys = rewards.keySet().iterator(); // Dispatch every listed message if (messages != null) { for (Message message : messages) { dispatchMessages(provider, formatter, message, player); } } // Handle reward specific messages for (int i = 0; i < generated.size() && rewardKeys.hasNext(); i++) { ResourceHolder element = generated.get(i); List<ResourceHolder> elements = Arrays.asList(generated.get(i)); List<Message> current = getMessages(rewardKeys.next()); // Print the messages (and ensure that the amount is greater than zero) if (current != null && element.getAmount() > 0) { for (Message message : current) { dispatchMessages(provider, formatter.createView(elements, null), message, player); } } } } /** * Dispatch a given message from a given player to the target channels. * @param provider - channel provider to use. * @param formatter - message formatter, complete with all the parameter information. * @param currentMessage - the message to transmit. * @param player - the sender, or NULL to simply announce the message. */ private void dispatchMessages(ChannelProvider provider, MessageFormatter formatter, Message currentMessage, Player player) { List<String> channels = getChannels(provider, currentMessage); List<String> failures = new ArrayList<String>(); ChannelService service = provider.getDefaultService(); if (channels != null && service != null) { // Like above, only without the player for (String channel : channels) { String text = currentMessage.getText(); try { if (service.hasChannel(channel)) { if (player == null) service.announce(channel, formatter.formatMessage(text)); else service.emote(channel, formatter.formatMessage(text), player); } else { failures.add(channel); } } catch (IllegalArgumentException e) { failures.add(channel); } } } // Error! if (debugger != null && !failures.isEmpty()) { debugger.printDebug(this, "Cannot find channels: %s", StringUtils.join(failures, ", ")); } } private List<String> getChannels(ChannelProvider provider, List<Message> messages) { List<String> result = new ArrayList<String>(); // Combine every channel into a big list for (Message message : messages) { List<String> channels = getChannels(provider, message); if (channels != null && channels.size() > 0) { result.addAll(channels); } } // Return NULL instead of an empty list if (result.size() != 0) return result; else return null; } private List<String> getChannels(ChannelProvider provider, Message message) { // Guard against NULL if (message == null) return null; // See if we can return a list of channels if (message.getChannels() != null) return message.getChannels(); else if (provider != null && provider.getDefaultChannels() != null) return provider.getDefaultChannels(); else return null; } /** * Retrieve the specific reward messages. * @param rewardName - name of the reward. * @return The reward messages, or NULL if they don't exist or the reward doesn't exist. */ public List<Message> getMessages(String rewardName) { MessagedResource resource = getEntry(rewardName, false); return resource != null ? resource.getMessages() : null; } /** * Retrieve the messages that will be sent when the action is performed. * @return The messages to send when the action triggers. */ public List<Message> getMessages() { return messages; } /** * Sets the messages that will be sent when the action is performed. * @param messages - Messages to send when the action triggers. */ public void setMessages(List<Message> messages) { this.messages = messages; } public int getId() { return id; } public void setId(int id) { this.id = id; } public Action multiply(double multiply) { Map<String, MessagedResource> copy = new HashMap<String, MessagedResource>(); // Multiply everything for (Map.Entry<String, MessagedResource> entry : rewards.entrySet()) { MessagedResource old = entry.getValue(); copy.put(entry.getKey(), old.multiply(multiply)); } // Copy everything Action action = new Action(messages, copy, debugger, id); action.setInheritMultiplier(inheritMultiplier); action.setInheritance(inherit); return action; } /** * Inherit traits from the previous action into the current action, returning a new action with the result. * @param previous - the previous action to inherit from. * @return A new action with the traits of this and the previois action. */ public Action inheritAction(Action previous) { // Scale the previous action Action scaled = previous.multiply(getInheritMultiplier()); Action current = multiply(1); // Include the previous multiplier current.setInheritMultiplier(current.getInheritMultiplier() * previous.getInheritMultiplier()); // Find any rewards that are not overwritten Collection<String> rewards = scaled.getRewardNames(); rewards.removeAll(getRewardNames()); // Copy over for (String reward : rewards) { current.addReward(reward, scaled.getReward(reward)); } // And copy the message too, if it hasn't already been set if (current.messages == null) { current.messages = scaled.messages; } return current; } /** * Retrieves the resource multiplier to use during inheritance, if any. * @return The resource multiplier. */ public double getInheritMultiplier() { return inheritMultiplier; } /** * Sets the resource multiplier to use during inheritance, if any. * @param inheritMultiplier - The new resource multiplier. */ public void setInheritMultiplier(double inheritMultiplier) { this.inheritMultiplier = inheritMultiplier; } /** * Whether or not this action should inherit rewards from any previous actions. * @return TRUE if inheritance is enabled, FALSE otherwise. */ public boolean hasInheritance() { return inherit; } /** * Sets whether or not this action should inherit rewards from any previous actions. * @param value - TRUE to use inheritance. */ public void setInheritance(boolean value) { this.inherit = value; } @Override public int hashCode() { return 17 * id; } @Override public boolean equals(Object obj) { if (obj == null) return false; if (obj == this) return true; if (obj.getClass() != getClass()) return false; Action other = (Action) obj; return new EqualsBuilder().append(messages, other.messages).append(rewards, other.rewards) .append(inherit, other.inherit).append(inheritMultiplier, other.inheritMultiplier) .append(id, other.id).isEquals(); } @Override public String toString() { List<String> textRewards = new ArrayList<String>(); // Build list of rewards for (Map.Entry<String, MessagedResource> entry : rewards.entrySet()) { String key = entry.getKey(); MessagedResource value = entry.getValue(); textRewards.add(String.format("%s: %s", key, value)); } return String.format("%s %s (%d)", StringUtils.join(textRewards, ", "), messages, id); } public Debugger getDebugger() { return debugger; } public void setDebugger(Debugger debugger) { this.debugger = debugger; } /** * Represents a resource factory that transmits a message when it produced a non-zero resource. * * @author Kristian */ private static class MessagedResource { private ResourceFactory resourceFactory; private List<Message> messages; public MessagedResource() { // Default values } private MessagedResource(ResourceFactory resourceFactory, List<Message> messages) { this.resourceFactory = resourceFactory; this.messages = messages; } public ResourceFactory getResourceFactory() { return resourceFactory; } public void setResourceFactory(ResourceFactory resourceFactory) { this.resourceFactory = resourceFactory; } public List<Message> getMessages() { return messages; } public void setMessages(List<Message> messages) { this.messages = messages; } private ResourceFactory getMultipliedFactory(double multiply) { if (resourceFactory != null) return resourceFactory.withMultiplier(resourceFactory.getMultiplier() * multiply); else return null; } public MessagedResource multiply(double multiply) { return new MessagedResource(getMultipliedFactory(multiply), messages); } @Override public int hashCode() { return new HashCodeBuilder(17, 31).append(resourceFactory).append(messages).toHashCode(); } @Override public boolean equals(Object obj) { if (obj == null) return false; if (obj == this) return true; if (obj.getClass() != getClass()) return false; MessagedResource other = (MessagedResource) obj; return new EqualsBuilder().append(resourceFactory, other.resourceFactory) .append(messages, other.messages).isEquals(); } @Override public String toString() { if (messages == null || messages.size() == 0) return String.format("%s", resourceFactory); else return String.format("%s [%s]", resourceFactory, StringUtils.join(messages, ", ")); } } }