summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/bukkit/World.java3
-rw-r--r--src/main/java/org/bukkit/block/Block.java3
-rw-r--r--src/main/java/org/bukkit/block/BlockState.java3
-rw-r--r--src/main/java/org/bukkit/entity/Entity.java3
-rw-r--r--src/main/java/org/bukkit/metadata/FixedMetadataValue.java25
-rw-r--r--src/main/java/org/bukkit/metadata/LazyMetadataValue.java158
-rw-r--r--src/main/java/org/bukkit/metadata/MetadataConversionException.java13
-rw-r--r--src/main/java/org/bukkit/metadata/MetadataEvaluationException.java13
-rw-r--r--src/main/java/org/bukkit/metadata/MetadataStore.java52
-rw-r--r--src/main/java/org/bukkit/metadata/MetadataStoreBase.java143
-rw-r--r--src/main/java/org/bukkit/metadata/MetadataValue.java73
-rw-r--r--src/main/java/org/bukkit/metadata/Metadatable.java42
-rw-r--r--src/main/java/org/bukkit/plugin/Plugin.java71
-rw-r--r--src/main/java/org/bukkit/plugin/java/JavaPlugin.java2
-rw-r--r--src/main/java/org/bukkit/util/NumberConversions.java78
-rw-r--r--src/test/java/org/bukkit/metadata/FixedMetadataValueTest.java30
-rw-r--r--src/test/java/org/bukkit/metadata/LazyMetadataValueTest.java135
-rw-r--r--src/test/java/org/bukkit/metadata/MetadataConversionTest.java103
-rw-r--r--src/test/java/org/bukkit/metadata/MetadataStoreTest.java127
-rw-r--r--src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java3
-rw-r--r--src/test/java/org/bukkit/plugin/messaging/TestPlayer.java17
-rw-r--r--src/test/java/org/bukkit/plugin/messaging/TestPlugin.java33
22 files changed, 1058 insertions, 72 deletions
diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
index 5212f961..1b326a55 100644
--- a/src/main/java/org/bukkit/World.java
+++ b/src/main/java/org/bukkit/World.java
@@ -13,13 +13,14 @@ import org.bukkit.block.Block;
import org.bukkit.entity.*;
import org.bukkit.generator.BlockPopulator;
import org.bukkit.inventory.ItemStack;
+import org.bukkit.metadata.Metadatable;
import org.bukkit.plugin.messaging.PluginMessageRecipient;
import org.bukkit.util.Vector;
/**
* Represents a world, which may contain entities, chunks and blocks
*/
-public interface World extends PluginMessageRecipient {
+public interface World extends PluginMessageRecipient, Metadatable {
/**
* Gets the {@link Block} at the given coordinates
diff --git a/src/main/java/org/bukkit/block/Block.java b/src/main/java/org/bukkit/block/Block.java
index 7ca3c01b..99bc5630 100644
--- a/src/main/java/org/bukkit/block/Block.java
+++ b/src/main/java/org/bukkit/block/Block.java
@@ -7,6 +7,7 @@ import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.Location;
import org.bukkit.inventory.ItemStack;
+import org.bukkit.metadata.Metadatable;
/**
* Represents a block. This is a live object, and only one Block may exist for
@@ -14,7 +15,7 @@ import org.bukkit.inventory.ItemStack;
* to your own handling of it; use block.getState() to get a snapshot state of a
* block which will not be modified.
*/
-public interface Block {
+public interface Block extends Metadatable {
/**
* Gets the metadata for this block
diff --git a/src/main/java/org/bukkit/block/BlockState.java b/src/main/java/org/bukkit/block/BlockState.java
index 7e181017..cd5bcfbf 100644
--- a/src/main/java/org/bukkit/block/BlockState.java
+++ b/src/main/java/org/bukkit/block/BlockState.java
@@ -5,6 +5,7 @@ import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.material.MaterialData;
+import org.bukkit.metadata.Metadatable;
/**
* Represents a captured state of a block, which will not change automatically.
@@ -14,7 +15,7 @@ import org.bukkit.material.MaterialData;
* the state of the block and you will not know, or they may change the block to
* another type entirely, causing your BlockState to become invalid.
*/
-public interface BlockState {
+public interface BlockState extends Metadatable {
/**
* Gets the block represented by this BlockState
diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
index c8554734..d9d219b3 100644
--- a/src/main/java/org/bukkit/entity/Entity.java
+++ b/src/main/java/org/bukkit/entity/Entity.java
@@ -5,6 +5,7 @@ import org.bukkit.EntityEffect;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.event.entity.EntityDamageEvent;
+import org.bukkit.metadata.Metadatable;
import org.bukkit.util.Vector;
import java.util.List;
@@ -14,7 +15,7 @@ import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
/**
* Represents a base entity in the world
*/
-public interface Entity {
+public interface Entity extends Metadatable {
/**
* Gets the entity's current position
diff --git a/src/main/java/org/bukkit/metadata/FixedMetadataValue.java b/src/main/java/org/bukkit/metadata/FixedMetadataValue.java
new file mode 100644
index 00000000..41f89a0e
--- /dev/null
+++ b/src/main/java/org/bukkit/metadata/FixedMetadataValue.java
@@ -0,0 +1,25 @@
+package org.bukkit.metadata;
+
+import org.bukkit.plugin.Plugin;
+
+import java.util.concurrent.Callable;
+
+/**
+ * A FixedMetadataValue is a special case metadata item that contains the same value forever after initialization.
+ * Invalidating a FixedMetadataValue has no affect.
+ */
+public class FixedMetadataValue extends LazyMetadataValue {
+ /**
+ * Initializes a FixedMetadataValue with an Object
+ *
+ * @param owningPlugin the {@link Plugin} that created this metadata value.
+ * @param value the value assigned to this metadata value.
+ */
+ public FixedMetadataValue(Plugin owningPlugin, final Object value) {
+ super(owningPlugin, CacheStrategy.CACHE_ETERNALLY, new Callable<Object>() {
+ public Object call() throws Exception {
+ return value;
+ }
+ });
+ }
+}
diff --git a/src/main/java/org/bukkit/metadata/LazyMetadataValue.java b/src/main/java/org/bukkit/metadata/LazyMetadataValue.java
new file mode 100644
index 00000000..cc0ba506
--- /dev/null
+++ b/src/main/java/org/bukkit/metadata/LazyMetadataValue.java
@@ -0,0 +1,158 @@
+package org.bukkit.metadata;
+
+import java.lang.ref.SoftReference;
+import java.util.concurrent.Callable;
+
+import org.apache.commons.lang.Validate;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.util.NumberConversions;
+
+/**
+ * The LazyMetadataValue class implements a type of metadata that is not computed until another plugin asks for it.
+ * By making metadata values lazy, no computation is done by the providing plugin until absolutely necessary (if ever).
+ * Additionally, LazyMetadataValue objects cache their values internally unless overridden by a {@link CacheStrategy}
+ * or invalidated at the individual or plugin level. Once invalidated, the LazyMetadataValue will recompute its value
+ * when asked.
+ */
+public class LazyMetadataValue implements MetadataValue {
+ private Callable<Object> lazyValue;
+ private CacheStrategy cacheStrategy;
+ private SoftReference<Object> internalValue = new SoftReference<Object>(null);
+ private Plugin owningPlugin;
+ private static final Object ACTUALLY_NULL = new Object();
+
+ /**
+ * Initialized a LazyMetadataValue object with the default CACHE_AFTER_FIRST_EVAL cache strategy.
+ *
+ * @param owningPlugin the {@link Plugin} that created this metadata value.
+ * @param lazyValue the lazy value assigned to this metadata value.
+ */
+ public LazyMetadataValue(Plugin owningPlugin, Callable<Object> lazyValue) {
+ this(owningPlugin, CacheStrategy.CACHE_AFTER_FIRST_EVAL, lazyValue);
+ }
+
+ /**
+ * Initializes a LazyMetadataValue object with a specific cache strategy.
+ *
+ * @param owningPlugin the {@link Plugin} that created this metadata value.
+ * @param cacheStrategy determines the rules for caching this metadata value.
+ * @param lazyValue the lazy value assigned to this metadata value.
+ */
+ public LazyMetadataValue(Plugin owningPlugin, CacheStrategy cacheStrategy, Callable<Object> lazyValue) {
+ Validate.notNull(owningPlugin, "owningPlugin cannot be null");
+ Validate.notNull(cacheStrategy, "cacheStrategy cannot be null");
+ Validate.notNull(lazyValue, "lazyValue cannot be null");
+
+ this.lazyValue = lazyValue;
+ this.owningPlugin = owningPlugin;
+ this.cacheStrategy = cacheStrategy;
+ }
+
+ public Plugin getOwningPlugin() {
+ return owningPlugin;
+ }
+
+ public Object value() {
+ eval();
+ Object value = internalValue.get();
+ if (value == ACTUALLY_NULL) {
+ return null;
+ }
+ return value;
+ }
+
+ public int asInt() {
+ return NumberConversions.toInt(value());
+ }
+
+ public float asFloat() {
+ return NumberConversions.toFloat(value());
+ }
+
+ public double asDouble() {
+ return NumberConversions.toDouble(value());
+ }
+
+ public long asLong() {
+ return NumberConversions.toLong(value());
+ }
+
+ public short asShort() {
+ return NumberConversions.toShort(value());
+ }
+
+ public byte asByte() {
+ return NumberConversions.toByte(value());
+ }
+
+ public boolean asBoolean() {
+ Object value = value();
+ if (value instanceof Boolean) {
+ return (Boolean) value;
+ }
+
+ if (value instanceof Number) {
+ return ((Number) value).intValue() != 0;
+ }
+
+ if (value instanceof String) {
+ return Boolean.parseBoolean((String) value);
+ }
+
+ return value != null;
+ }
+
+ public String asString() {
+ Object value = value();
+
+ if (value == null) {
+ return "";
+ }
+ return value.toString();
+ }
+
+ /**
+ * Lazily evaluates the value of this metadata item.
+ *
+ * @throws MetadataEvaluationException if computing the metadata value fails.
+ */
+ private synchronized void eval() throws MetadataEvaluationException {
+ if (cacheStrategy == CacheStrategy.NEVER_CACHE || internalValue.get() == null) {
+ try {
+ Object value = lazyValue.call();
+ if (value == null) {
+ value = ACTUALLY_NULL;
+ }
+ internalValue = new SoftReference<Object>(value);
+ } catch (Exception e) {
+ throw new MetadataEvaluationException(e);
+ }
+ }
+ }
+
+ public synchronized void invalidate() {
+ if (cacheStrategy != CacheStrategy.CACHE_ETERNALLY) {
+ internalValue.clear();
+ }
+ }
+
+ /**
+ * Describes possible caching strategies for metadata.
+ */
+ public enum CacheStrategy {
+ /**
+ * Once the metadata value has been evaluated, do not re-evaluate the value until it is manually invalidated.
+ */
+ CACHE_AFTER_FIRST_EVAL,
+
+ /**
+ * Re-evaluate the metadata item every time it is requested
+ */
+ NEVER_CACHE,
+
+ /**
+ * Once the metadata value has been evaluated, do not re-evaluate the value in spite of manual invalidation.
+ */
+ CACHE_ETERNALLY
+ }
+}
diff --git a/src/main/java/org/bukkit/metadata/MetadataConversionException.java b/src/main/java/org/bukkit/metadata/MetadataConversionException.java
new file mode 100644
index 00000000..ff4d1205
--- /dev/null
+++ b/src/main/java/org/bukkit/metadata/MetadataConversionException.java
@@ -0,0 +1,13 @@
+package org.bukkit.metadata;
+
+/**
+ * A MetadataConversionException is thrown any time a {@link LazyMetadataValue} attempts to convert a metadata value
+ * to an inappropriate data type.
+ */
+
+@SuppressWarnings("serial")
+public class MetadataConversionException extends RuntimeException {
+ MetadataConversionException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/org/bukkit/metadata/MetadataEvaluationException.java b/src/main/java/org/bukkit/metadata/MetadataEvaluationException.java
new file mode 100644
index 00000000..6b5ac886
--- /dev/null
+++ b/src/main/java/org/bukkit/metadata/MetadataEvaluationException.java
@@ -0,0 +1,13 @@
+package org.bukkit.metadata;
+
+/**
+ * A MetadataEvaluationException is thrown any time a {@link LazyMetadataValue} fails to evaluate its value due to
+ * an exception. The originating exception will be included as this exception's cause.
+ */
+
+@SuppressWarnings("serial")
+public class MetadataEvaluationException extends RuntimeException {
+ MetadataEvaluationException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/org/bukkit/metadata/MetadataStore.java b/src/main/java/org/bukkit/metadata/MetadataStore.java
new file mode 100644
index 00000000..0adfa7a0
--- /dev/null
+++ b/src/main/java/org/bukkit/metadata/MetadataStore.java
@@ -0,0 +1,52 @@
+package org.bukkit.metadata;
+
+import org.bukkit.plugin.Plugin;
+
+import java.util.List;
+
+public interface MetadataStore<T> {
+ /**
+ * Adds a metadata value to an object.
+ *
+ * @param subject The object receiving the metadata.
+ * @param metadataKey A unique key to identify this metadata.
+ * @param newMetadataValue The metadata value to apply.
+ */
+ public void setMetadata(T subject, String metadataKey, MetadataValue newMetadataValue);
+
+ /**
+ * Returns all metadata values attached to an object. If multiple plugins have attached metadata, each will value
+ * will be included.
+ *
+ * @param subject the object being interrogated.
+ * @param metadataKey the unique metadata key being sought.
+ * @return A list of values, one for each plugin that has set the requested value.
+ */
+ public List<MetadataValue> getMetadata(T subject, String metadataKey);
+
+ /**
+ * Tests to see if a metadata attribute has been set on an object.
+ *
+ * @param subject the object upon which the has-metadata test is performed.
+ * @param metadataKey the unique metadata key being queried.
+ * @return the existence of the metadataKey within subject.
+ */
+ public boolean hasMetadata(T subject, String metadataKey);
+
+ /**
+ * Removes a metadata item owned by a plugin from a subject.
+ *
+ * @param subject the object to remove the metadata from.
+ * @param metadataKey the unique metadata key identifying the metadata to remove.
+ * @param owningPlugin the plugin attempting to remove a metadata item.
+ */
+ public void removeMetadata(T subject, String metadataKey, Plugin owningPlugin);
+
+ /**
+ * Invalidates all metadata in the metadata store that originates from the given plugin. Doing this will force
+ * each invalidated metadata item to be recalculated the next time it is accessed.
+ *
+ * @param owningPlugin the plugin requesting the invalidation.
+ */
+ public void invalidateAll(Plugin owningPlugin);
+}
diff --git a/src/main/java/org/bukkit/metadata/MetadataStoreBase.java b/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
new file mode 100644
index 00000000..0c384abe
--- /dev/null
+++ b/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
@@ -0,0 +1,143 @@
+package org.bukkit.metadata;
+
+import org.bukkit.plugin.Plugin;
+
+import java.util.*;
+
+public abstract class MetadataStoreBase<T> {
+ private Map<String, List<MetadataValue>> metadataMap = new HashMap<String, List<MetadataValue>>();
+ private WeakHashMap<T, String> disambiguationCache = new WeakHashMap<T, String>();
+
+ /**
+ * Adds a metadata value to an object. Each metadata value is owned by a specific{@link Plugin}.
+ * If a plugin has already added a metadata value to an object, that value
+ * will be replaced with the value of {@code newMetadataValue}. Multiple plugins can set independent values for
+ * the same {@code metadataKey} without conflict.
+ *
+ * Implementation note: I considered using a {@link java.util.concurrent.locks.ReadWriteLock} for controlling
+ * access to {@code metadataMap}, but decided that the added overhead wasn't worth the finer grained access control.
+ * Bukkit is almost entirely single threaded so locking overhead shouldn't pose a problem.
+ *
+ * @see MetadataStore#setMetadata(Object, String, MetadataValue)
+ * @param subject The object receiving the metadata.
+ * @param metadataKey A unique key to identify this metadata.
+ * @param newMetadataValue The metadata value to apply.
+ */
+ public synchronized void setMetadata(T subject, String metadataKey, MetadataValue newMetadataValue) {
+ String key = cachedDisambiguate(subject, metadataKey);
+ if (!metadataMap.containsKey(key)) {
+ metadataMap.put(key, new ArrayList<MetadataValue>());
+ }
+ // we now have a list of subject's metadata for the given metadata key. If newMetadataValue's owningPlugin
+ // is found in this list, replace the value rather than add a new one.
+ List<MetadataValue> metadataList = metadataMap.get(key);
+ for (int i = 0; i < metadataList.size(); i++) {
+ if (metadataList.get(i).getOwningPlugin().equals(newMetadataValue.getOwningPlugin())) {
+ metadataList.set(i, newMetadataValue);
+ return;
+ }
+ }
+ // we didn't find a duplicate...add the new metadata value
+ metadataList.add(newMetadataValue);
+ }
+
+ /**
+ * Returns all metadata values attached to an object. If multiple plugins have attached metadata, each will value
+ * will be included.
+ *
+ * @see MetadataStore#getMetadata(Object, String)
+ * @param subject the object being interrogated.
+ * @param metadataKey the unique metadata key being sought.
+ * @return A list of values, one for each plugin that has set the requested value.
+ */
+ public synchronized List<MetadataValue> getMetadata(T subject, String metadataKey) {
+ String key = cachedDisambiguate(subject, metadataKey);
+ if (metadataMap.containsKey(key)) {
+ return Collections.unmodifiableList(metadataMap.get(key));
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ /**
+ * Tests to see if a metadata attribute has been set on an object.
+ *
+ * @param subject the object upon which the has-metadata test is performed.
+ * @param metadataKey the unique metadata key being queried.
+ * @return the existence of the metadataKey within subject.
+ */
+ public synchronized boolean hasMetadata(T subject, String metadataKey) {
+ String key = cachedDisambiguate(subject, metadataKey);
+ return metadataMap.containsKey(key);
+ }
+
+ /**
+ * Removes a metadata item owned by a plugin from a subject.
+ *
+ * @see MetadataStore#removeMetadata(Object, String, org.bukkit.plugin.Plugin)
+ * @param subject the object to remove the metadata from.
+ * @param metadataKey the unique metadata key identifying the metadata to remove.
+ * @param owningPlugin the plugin attempting to remove a metadata item.
+ */
+ public synchronized void removeMetadata(T subject, String metadataKey, Plugin owningPlugin) {
+ String key = cachedDisambiguate(subject, metadataKey);
+ List<MetadataValue> metadataList = metadataMap.get(key);
+ for (int i = 0; i < metadataList.size(); i++) {
+ if (metadataList.get(i).getOwningPlugin().equals(owningPlugin)) {
+ metadataList.remove(i);
+ }
+ }
+ }
+
+ /**
+ * Invalidates all metadata in the metadata store that originates from the given plugin. Doing this will force
+ * each invalidated metadata item to be recalculated the next time it is accessed.
+ *
+ * @see MetadataStore#invalidateAll(org.bukkit.plugin.Plugin)
+ * @param owningPlugin the plugin requesting the invalidation.
+ */
+ public synchronized void invalidateAll(Plugin owningPlugin) {
+ if(owningPlugin == null) {
+ throw new IllegalArgumentException("owningPlugin cannot be null");
+ }
+
+ for (List<MetadataValue> values : metadataMap.values()) {
+ for (MetadataValue value : values) {
+ if (value.getOwningPlugin().equals(owningPlugin)) {
+ value.invalidate();
+ }
+ }
+ }
+ }
+
+ /**
+ * Caches the results of calls to {@link MetadataStoreBase#disambiguate(Object, String)} in a {@link WeakHashMap}. Doing so maintains a
+ * <a href="http://www.codeinstructions.com/2008/09/weakhashmap-is-not-cache-understanding.html">canonical list</a>
+ * of disambiguation strings for objects in memory. When those objects are garbage collected, the disambiguation string
+ * in the list is aggressively garbage collected as well.
+ * @param subject The object for which this key is being generated.
+ * @param metadataKey The name identifying the metadata value.
+ * @return a unique metadata key for the given subject.
+ */
+ private String cachedDisambiguate(T subject, String metadataKey) {
+ if (disambiguationCache.containsKey(subject)) {
+ return disambiguationCache.get(subject);
+ } else {
+ String disambiguation = disambiguate(subject, metadataKey);
+ disambiguationCache.put(subject, disambiguation);
+ return disambiguation;
+ }
+ }
+
+ /**
+ * Creates a unique name for the object receiving metadata by combining unique data from the subject with a metadataKey.
+ * The name created must be globally unique for the given object and any two equivalent objects must generate the
+ * same unique name. For example, two Player objects must generate the same string if they represent the same player,
+ * even if the objects would fail a reference equality test.
+ *
+ * @param subject The object for which this key is being generated.
+ * @param metadataKey The name identifying the metadata value.
+ * @return a unique metadata key for the given subject.
+ */
+ protected abstract String disambiguate(T subject, String metadataKey);
+}
diff --git a/src/main/java/org/bukkit/metadata/MetadataValue.java b/src/main/java/org/bukkit/metadata/MetadataValue.java
new file mode 100644
index 00000000..761d1ac9
--- /dev/null
+++ b/src/main/java/org/bukkit/metadata/MetadataValue.java
@@ -0,0 +1,73 @@
+package org.bukkit.metadata;
+
+import org.bukkit.plugin.Plugin;
+
+public interface MetadataValue {
+
+ /**
+ * Fetches the value of this metadata item.
+ *
+ * @return the metadata value.
+ */
+ public Object value();
+
+ /**
+ * Attempts to convert the value of this metadata item into an int.
+ * @return the value as an int.
+ */
+ public int asInt();
+
+ /**
+ * Attempts to convert the value of this metadata item into a float.
+ * @return the value as a float.
+ */
+ public float asFloat();
+
+ /**
+ * Attempts to convert the value of this metadata item into a double.
+ * @return the value as a double.
+ */
+ public double asDouble();
+
+ /**
+ * Attempts to convert the value of this metadata item into a long.
+ * @return the value as a long.
+ */
+ public long asLong();
+
+ /**
+ * Attempts to convert the value of this metadata item into a short.
+ * @return the value as a short.
+ */
+ public short asShort();
+
+ /**
+ * Attempts to convert the value of this metadata item into a byte.
+ * @return the value as a byte.
+ */
+ public byte asByte();
+
+ /**
+ * Attempts to convert the value of this metadata item into a boolean.
+ * @return the value as a boolean.
+ */
+ public boolean asBoolean();
+
+ /**
+ * Attempts to convert the value of this metadata item into a string.
+ * @return the value as a string.
+ */
+ public String asString();
+
+ /**
+ * Returns the {@link Plugin} that created this metadata item.
+ *
+ * @return the plugin that owns this metadata value.
+ */
+ public Plugin getOwningPlugin();
+
+ /**
+ * Invalidates this metadata item, forcing it to recompute when next accessed.
+ */
+ public void invalidate();
+}
diff --git a/src/main/java/org/bukkit/metadata/Metadatable.java b/src/main/java/org/bukkit/metadata/Metadatable.java
new file mode 100644
index 00000000..956a1960
--- /dev/null
+++ b/src/main/java/org/bukkit/metadata/Metadatable.java
@@ -0,0 +1,42 @@
+package org.bukkit.metadata;
+
+import org.bukkit.plugin.Plugin;
+
+import java.util.List;
+
+/**
+ * This interface is implemented by all objects that can provide metadata about themselves.
+ */
+public interface Metadatable {
+ /**
+ * Sets a metadata value in the implementing object's metadata store.
+ *
+ * @param metadataKey A unique key to identify this metadata.
+ * @param newMetadataValue The metadata value to apply.
+ */
+ public void setMetadata(String metadataKey, MetadataValue newMetadataValue);
+
+ /**
+ * Returns a list of previously set metadata values from the implementing object's metadata store.
+ *
+ * @param metadataKey the unique metadata key being sought.
+ * @return A list of values, one for each plugin that has set the requested value.
+ */
+ public List<MetadataValue> getMetadata(String metadataKey);
+
+ /**
+ * Tests to see whether the implementing object contains the given metadata value in its metadata store.
+ *
+ * @param metadataKey the unique metadata key being queried.
+ * @return the existence of the metadataKey within subject.
+ */
+ public boolean hasMetadata(String metadataKey);
+
+ /**
+ * Removes the given metadata value from the implementing object's metadata store.
+ *
+ * @param metadataKey the unique metadata key identifying the metadata to remove.
+ * @param owningPlugin This plugin's metadata value will be removed. All other values will be left untouched.
+ */
+ public void removeMetadata(String metadataKey, Plugin owningPlugin);
+}
diff --git a/src/main/java/org/bukkit/plugin/Plugin.java b/src/main/java/org/bukkit/plugin/Plugin.java
index 450252d8..97c882aa 100644
--- a/src/main/java/org/bukkit/plugin/Plugin.java
+++ b/src/main/java/org/bukkit/plugin/Plugin.java
@@ -14,22 +14,21 @@ import com.avaje.ebean.EbeanServer;
/**
* Represents a Plugin
*/
-public interface Plugin extends CommandExecutor {
-
+public abstract class Plugin implements CommandExecutor {
/**
* Returns the folder that the plugin data's files are located in. The
* folder may not yet exist.
*
* @return The folder
*/
- public File getDataFolder();
+ public abstract File getDataFolder();
/**
* Returns the plugin.yaml file containing the details for this plugin
*
* @return Contents of the plugin.yaml file
*/
- public PluginDescriptionFile getDescription();
+ public abstract PluginDescriptionFile getDescription();
/**
* Gets a {@link FileConfiguration} for this plugin, read through "config.yml"
@@ -39,7 +38,7 @@ public interface Plugin extends CommandExecutor {
*
* @return Plugin configuration
*/
- public FileConfiguration getConfig();
+ public abstract FileConfiguration getConfig();
/**
* Gets an embedded resource in this plugin
@@ -47,18 +46,18 @@ public interface Plugin extends CommandExecutor {
* @param filename Filename of the resource
* @return File if found, otherwise null
*/
- public InputStream getResource(String filename);
+ public abstract InputStream getResource(String filename);
/**
* Saves the {@link FileConfiguration} retrievable by {@link #getConfig()}.
*/
- public void saveConfig();
+ public abstract void saveConfig();
/**
* Saves the raw contents of the default config.yml file to the location retrievable by {@link #getConfig()}.
* If there is no default config.yml embedded in the plugin, an empty config.yml file is saved.
*/
- public void saveDefaultConfig();
+ public abstract void saveDefaultConfig();
/**
* Saves the raw contents of any resource embedded with a plugin's .jar file assuming it can be found using
@@ -69,70 +68,70 @@ public interface Plugin extends CommandExecutor {
* @param replace if true, the embedded resource will overwrite the contents of an existing file.
* @throws IllegalArgumentException if the resource path is null, empty, or points to a nonexistent resource.
*/
- public void saveResource(String resourcePath, boolean replace);
+ public abstract void saveResource(String resourcePath, boolean replace);
/**
* Discards any data in {@link #getConfig()} and reloads from disk.
*/
- public void reloadConfig();
+ public abstract void reloadConfig();
/**
* Gets the associated PluginLoader responsible for this plugin
*
* @return PluginLoader that controls this plugin
*/
- public PluginLoader getPluginLoader();
+ public abstract PluginLoader getPluginLoader();
/**
* Returns the Server instance currently running this plugin
*
* @return Server running this plugin
*/
- public Server getServer();
+ public abstract Server getServer();
/**
* Returns a value indicating whether or not this plugin is currently enabled
*
* @return true if this plugin is enabled, otherwise false
*/
- public boolean isEnabled();
+ public abstract boolean isEnabled();
/**
* Called when this plugin is disabled
*/
- public void onDisable();
+ public abstract void onDisable();
/**
* Called after a plugin is loaded but before it has been enabled.
* When mulitple plugins are loaded, the onLoad() for all plugins is called before any onEnable() is called.
*/
- public void onLoad();
+ public abstract void onLoad();
/**
* Called when this plugin is enabled
*/
- public void onEnable();
+ public abstract void onEnable();
/**
* Simple boolean if we can still nag to the logs about things
*
* @return boolean whether we can nag
*/
- public boolean isNaggable();
+ public abstract boolean isNaggable();
/**
* Set naggable state
*
* @param canNag is this plugin still naggable?
*/
- public void setNaggable(boolean canNag);
+ public abstract void setNaggable(boolean canNag);
/**
* Gets the {@link EbeanServer} tied to this plugin
*
* @return Ebean server instance
*/
- public EbeanServer getDatabase();
+ public abstract EbeanServer getDatabase();
/**
* Gets a {@link ChunkGenerator} for use in a default world, as specified in the server configuration
@@ -141,7 +140,7 @@ public interface Plugin extends CommandExecutor {
* @param id Unique ID, if any, that was specified to indicate which generator was requested
* @return ChunkGenerator for use in the default world generation
*/
- public ChunkGenerator getDefaultWorldGenerator(String worldName, String id);
+ public abstract ChunkGenerator getDefaultWorldGenerator(String worldName, String id);
/**
* Returns the primary logger associated with this server instance. The returned logger automatically
@@ -149,5 +148,35 @@ public interface Plugin extends CommandExecutor {
*
* @return Logger associated with this server
*/
- public Logger getLogger();
+ public abstract Logger getLogger();
+
+ /**
+ * Returns the name of the plugin.
+ *
+ * This should return the bare name of the plugin and should be used for comparison.
+ *
+ * @return name of the plugin
+ */
+ public String getName() {
+ return getDescription().getName();
+ }
+
+ @Override
+ public int hashCode() {
+ return getName().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof Plugin)) {
+ return false;
+ }
+ return getName().equals(((Plugin) obj).getName());
+ }
}
diff --git a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
index 10b19e91..6cc7d9db 100644
--- a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
+++ b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
@@ -34,7 +34,7 @@ import com.avaje.ebeaninternal.server.ddl.DdlGenerator;
/**
* Represents a Java plugin
*/
-public abstract class JavaPlugin implements Plugin {
+public abstract class JavaPlugin extends Plugin {
private boolean isEnabled = false;
private boolean initialized = false;
private PluginLoader loader = null;
diff --git a/src/main/java/org/bukkit/util/NumberConversions.java b/src/main/java/org/bukkit/util/NumberConversions.java
index f70cc314..9c0b132f 100644
--- a/src/main/java/org/bukkit/util/NumberConversions.java
+++ b/src/main/java/org/bukkit/util/NumberConversions.java
@@ -9,84 +9,78 @@ public final class NumberConversions {
public static int toInt(Object object) {
if (object instanceof Number) {
return ((Number) object).intValue();
- } else {
- int result = 0;
-
- try {
- result = Integer.parseInt(object.toString());
- } catch (NumberFormatException ex) {}
+ }
- return result;
+ try {
+ return Integer.valueOf(object.toString());
+ } catch (NumberFormatException e) {
+ } catch (NullPointerException e) {
}
+ return 0;
}
public static float toFloat(Object object) {
if (object instanceof Number) {
return ((Number) object).floatValue();
- } else {
- float result = 0;
-
- try {
- result = Float.parseFloat(object.toString());
- } catch (NumberFormatException ex) {}
+ }
- return result;
+ try {
+ return Float.valueOf(object.toString());
+ } catch (NumberFormatException e) {
+ } catch (NullPointerException e) {
}
+ return 0;
}
public static double toDouble(Object object) {
if (object instanceof Number) {
return ((Number) object).doubleValue();
- } else {
- double result = 0;
-
- try {
- result = Double.parseDouble(object.toString());
- } catch (NumberFormatException ex) {}
+ }
- return result;
+ try {
+ return Double.valueOf(object.toString());
+ } catch (NumberFormatException e) {
+ } catch (NullPointerException e) {
}
+ return 0;
}
public static long toLong(Object object) {
if (object instanceof Number) {
return ((Number) object).longValue();
- } else {
- long result = 0;
-
- try {
- result = Long.parseLong(object.toString());
- } catch (NumberFormatException ex) {}
+ }
- return result;
+ try {
+ return Long.valueOf(object.toString());
+ } catch (NumberFormatException e) {
+ } catch (NullPointerException e) {
}
+ return 0;
}
public static short toShort(Object object) {
if (object instanceof Number) {
return ((Number) object).shortValue();
- } else {
- short result = 0;
-
- try {
- result = Short.parseShort(object.toString());
- } catch (NumberFormatException ex) {}
+ }
- return result;
+ try {
+ return Short.valueOf(object.toString());
+ } catch (NumberFormatException e) {
+ } catch (NullPointerException e) {
}
+ return 0;
}
public static byte toByte(Object object) {
if (object instanceof Number) {
return ((Number) object).byteValue();
- } else {
- byte result = 0;
-
- try {
- result = Byte.parseByte(object.toString());
- } catch (NumberFormatException ex) {}
+ }
- return result;
+ try {
+ return Byte.valueOf(object.toString());
+ } catch (NumberFormatException e) {
+ } catch (NullPointerException e) {
}
+ return 0;
}
}
diff --git a/src/test/java/org/bukkit/metadata/FixedMetadataValueTest.java b/src/test/java/org/bukkit/metadata/FixedMetadataValueTest.java
new file mode 100644
index 00000000..0844f975
--- /dev/null
+++ b/src/test/java/org/bukkit/metadata/FixedMetadataValueTest.java
@@ -0,0 +1,30 @@
+package org.bukkit.metadata;
+
+import static org.junit.Assert.assertEquals;
+
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.messaging.TestPlugin;
+import org.junit.Test;
+
+public class FixedMetadataValueTest {
+ private Plugin plugin = new TestPlugin("X");
+ private FixedMetadataValue subject;
+
+ private void valueEquals(Object value) {
+ subject = new FixedMetadataValue(plugin, value);
+ assertEquals(value, subject.value());
+ }
+
+ @Test
+ public void testTypes() {
+ valueEquals(10);
+ valueEquals(0.1);
+ valueEquals("TEN");
+ valueEquals(true);
+ valueEquals(null);
+ valueEquals((float) 10.5);
+ valueEquals((long) 10);
+ valueEquals((short) 10);
+ valueEquals((byte) 10);
+ }
+}
diff --git a/src/test/java/org/bukkit/metadata/LazyMetadataValueTest.java b/src/test/java/org/bukkit/metadata/LazyMetadataValueTest.java
new file mode 100644
index 00000000..ece6a0f4
--- /dev/null
+++ b/src/test/java/org/bukkit/metadata/LazyMetadataValueTest.java
@@ -0,0 +1,135 @@
+package org.bukkit.metadata;
+
+import org.bukkit.plugin.messaging.TestPlugin;
+import org.junit.Test;
+
+import java.util.concurrent.Callable;
+
+import static org.junit.Assert.*;
+
+public class LazyMetadataValueTest {
+ private LazyMetadataValue subject;
+ private TestPlugin plugin = new TestPlugin("x");
+
+ @Test
+ public void testLazyInt() {
+ int value = 10;
+ subject = makeSimpleCallable(value);
+
+ assertEquals(value, subject.value());
+ }
+
+ @Test
+ public void testLazyDouble() {
+ double value = 10.5;
+ subject = makeSimpleCallable(value);
+
+ assertEquals(value, (Double)subject.value(), 0.01);
+ }
+
+ @Test
+ public void testLazyString() {
+ String value = "TEN";
+ subject = makeSimpleCallable(value);
+
+ assertEquals(value, subject.value());
+ }
+
+ @Test
+ public void testLazyBoolean() {
+ boolean value = false;
+ subject = makeSimpleCallable(value);
+
+ assertEquals(value, subject.value());
+ }
+
+ @Test(expected=MetadataEvaluationException.class)
+ public void testEvalException() {
+ subject = new LazyMetadataValue(plugin, LazyMetadataValue.CacheStrategy.CACHE_AFTER_FIRST_EVAL, new Callable<Object>() {
+ public Object call() throws Exception {
+ throw new RuntimeException("Gotcha!");
+ }
+ });
+ subject.value();
+ }
+
+ @Test
+ public void testCacheStrategyCacheAfterFirstEval() {
+ final Counter counter = new Counter();
+ final int value = 10;
+ subject = new LazyMetadataValue(plugin, LazyMetadataValue.CacheStrategy.CACHE_AFTER_FIRST_EVAL, new Callable<Object>() {
+ public Object call() throws Exception {
+ counter.increment();
+ return value;
+ }
+ });
+
+ subject.value();
+ subject.value();
+ assertEquals(value, subject.value());
+ assertEquals(1, counter.value());
+
+ subject.invalidate();
+ subject.value();
+ assertEquals(2, counter.value());
+ }
+
+ @Test
+ public void testCacheStrategyNeverCache() {
+ final Counter counter = new Counter();
+ final int value = 10;
+ subject = new LazyMetadataValue(plugin, LazyMetadataValue.CacheStrategy.NEVER_CACHE, new Callable<Object>() {
+ public Object call() throws Exception {
+ counter.increment();
+ return value;
+ }
+ });
+
+ subject.value();
+ subject.value();
+ assertEquals(value, subject.value());
+ assertEquals(3, counter.value());
+ }
+
+ @Test
+ public void testCacheStrategyEternally() {
+ final Counter counter = new Counter();
+ final int value = 10;
+ subject = new LazyMetadataValue(plugin, LazyMetadataValue.CacheStrategy.CACHE_ETERNALLY, new Callable<Object>() {
+ public Object call() throws Exception {
+ counter.increment();
+ return value;
+ }
+ });
+
+ subject.value();
+ subject.value();
+ assertEquals(value, subject.value());
+ assertEquals(1, counter.value());
+
+ subject.invalidate();
+ subject.value();
+ assertEquals(value, subject.value());
+ assertEquals(1, counter.value());
+ }
+
+ private LazyMetadataValue makeSimpleCallable(final Object value) {
+ return new LazyMetadataValue(plugin, new Callable<Object>() {
+ public Object call() throws Exception {
+ return value;
+ }
+ });
+ }
+
+ private class Counter {
+ private int c = 0;
+
+ public void increment() {
+ c++;
+ }
+
+ public int value() {
+ return c;
+ }
+ }
+}
diff --git a/src/test/java/org/bukkit/metadata/MetadataConversionTest.java b/src/test/java/org/bukkit/metadata/MetadataConversionTest.java
new file mode 100644
index 00000000..428768ad
--- /dev/null
+++ b/src/test/java/org/bukkit/metadata/MetadataConversionTest.java
@@ -0,0 +1,103 @@
+// Copyright (C) 2011 Ryan Michela
+//
+// 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 3 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, see <http://www.gnu.org/licenses/>.
+
+package org.bukkit.metadata;
+
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.messaging.TestPlugin;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ */
+public class MetadataConversionTest {
+ private Plugin plugin = new TestPlugin("x");
+ private FixedMetadataValue subject;
+
+ private void setSubject(Object value) {
+ subject = new FixedMetadataValue(plugin, value);
+ }
+
+ @Test
+ public void testFromInt() {
+ setSubject(10);
+
+ assertEquals(10, subject.asInt());
+ assertEquals(10, subject.asFloat(), 0.000001);
+ assertEquals(10, subject.asDouble(), 0.000001);
+ assertEquals(10, subject.asLong());
+ assertEquals(10, subject.asShort());
+ assertEquals(10, subject.asByte());
+ assertEquals(true, subject.asBoolean());
+ assertEquals("10", subject.asString());
+ }
+
+ @Test
+ public void testFromFloat() {
+ setSubject(10.5);
+
+ assertEquals(10, subject.asInt());
+ assertEquals(10.5, subject.asFloat(), 0.000001);
+ assertEquals(10.5, subject.asDouble(), 0.000001);
+ assertEquals(10, subject.asLong());
+ assertEquals(10, subject.asShort());
+ assertEquals(10, subject.asByte());
+ assertEquals(true, subject.asBoolean());
+ assertEquals("10.5", subject.asString());
+ }
+
+ @Test
+ public void testFromNumericString() {
+ setSubject("10");
+
+ assertEquals(10, subject.asInt());
+ assertEquals(10, subject.asFloat(), 0.000001);
+ assertEquals(10, subject.asDouble(), 0.000001);
+ assertEquals(10, subject.asLong());
+ assertEquals(10, subject.asShort());
+ assertEquals(10, subject.asByte());
+ assertEquals(false, subject.asBoolean());
+ assertEquals("10", subject.asString());
+ }
+
+ @Test
+ public void testFromNonNumericString() {
+ setSubject("true");
+
+ assertEquals(0, subject.asInt());
+ assertEquals(0, subject.asFloat(), 0.000001);
+ assertEquals(0, subject.asDouble(), 0.000001);
+ assertEquals(0, subject.asLong());
+ assertEquals(0, subject.asShort());
+ assertEquals(0, subject.asByte());
+ assertEquals(true, subject.asBoolean());
+ assertEquals("true", subject.asString());
+ }
+
+ @Test
+ public void testFromNull() {
+ setSubject(null);
+
+ assertEquals(0, subject.asInt());
+ assertEquals(0, subject.asFloat(), 0.000001);
+ assertEquals(0, subject.asDouble(), 0.000001);
+ assertEquals(0, subject.asLong());
+ assertEquals(0, subject.asShort());
+ assertEquals(0, subject.asByte());
+ assertEquals(false, subject.asBoolean());
+ assertEquals("", subject.asString());
+ }
+}
diff --git a/src/test/java/org/bukkit/metadata/MetadataStoreTest.java b/src/test/java/org/bukkit/metadata/MetadataStoreTest.java
new file mode 100644
index 00000000..98a96423
--- /dev/null
+++ b/src/test/java/org/bukkit/metadata/MetadataStoreTest.java
@@ -0,0 +1,127 @@
+package org.bukkit.metadata;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.messaging.TestPlugin;
+import org.junit.Test;
+
+public class MetadataStoreTest {
+ private Plugin pluginX = new TestPlugin("x");
+ private Plugin pluginY = new TestPlugin("y");
+
+ StringMetadataStore subject = new StringMetadataStore();
+
+ @Test
+ public void testMetadataStore() {
+ subject.setMetadata("subject", "key", new FixedMetadataValue(pluginX, 10));
+
+ assertTrue(subject.hasMetadata("subject", "key"));
+ List<MetadataValue> values = subject.getMetadata("subject", "key");
+ assertEquals(10, values.get(0).value());
+ }
+
+ @Test
+ public void testMetadataNotPresent() {
+ assertFalse(subject.hasMetadata("subject", "key"));
+ List<MetadataValue> values = subject.getMetadata("subject", "key");
+ assertTrue(values.isEmpty());
+ }
+
+ @Test
+ public void testInvalidateAll() {
+ final Counter counter = new Counter();
+
+ subject.setMetadata("subject", "key", new LazyMetadataValue(pluginX, new Callable<Object>() {
+ public Object call() throws Exception {
+ counter.increment();
+ return 10;
+ }
+ }));
+
+ assertTrue(subject.hasMetadata("subject", "key"));
+ subject.getMetadata("subject", "key").get(0).value();
+ subject.invalidateAll(pluginX);
+ subject.getMetadata("subject", "key").get(0).value();
+ assertEquals(2, counter.value());
+ }
+
+ @Test
+ public void testInvalidateAllButActuallyNothing() {
+ final Counter counter = new Counter();
+
+ subject.setMetadata("subject", "key", new LazyMetadataValue(pluginX, new Callable<Object>() {
+ public Object call() throws Exception {
+ counter.increment();
+ return 10;
+ }
+ }));
+
+ assertTrue(subject.hasMetadata("subject", "key"));
+ subject.getMetadata("subject", "key").get(0).value();
+ subject.invalidateAll(pluginY);
+ subject.getMetadata("subject", "key").get(0).value();
+ assertEquals(1, counter.value());
+ }
+
+ @Test
+ public void testMetadataReplace() {
+ subject.setMetadata("subject", "key", new FixedMetadataValue(pluginX, 10));
+ subject.setMetadata("subject", "key", new FixedMetadataValue(pluginY, 10));
+ subject.setMetadata("subject", "key", new FixedMetadataValue(pluginX, 20));
+
+ for (MetadataValue mv : subject.getMetadata("subject", "key")) {
+ if (mv.getOwningPlugin().equals(pluginX)) {
+ assertEquals(20, mv.value());
+ }
+ if (mv.getOwningPlugin().equals(pluginY)) {
+ assertEquals(10, mv.value());
+ }
+ }
+ }
+
+ @Test
+ public void testMetadataRemove() {
+ subject.setMetadata("subject", "key", new FixedMetadataValue(pluginX, 10));
+ subject.setMetadata("subject", "key", new FixedMetadataValue(pluginY, 20));
+ subject.removeMetadata("subject", "key", pluginX);
+
+ assertTrue(subject.hasMetadata("subject", "key"));
+ assertEquals(1, subject.getMetadata("subject", "key").size());
+ assertEquals(20, subject.getMetadata("subject", "key").get(0).value());
+ }
+
+ @Test
+ public void testMetadataRemoveForNonExistingPlugin() {
+ subject.setMetadata("subject", "key", new FixedMetadataValue(pluginX, 10));
+ subject.removeMetadata("subject", "key", pluginY);
+
+ assertTrue(subject.hasMetadata("subject", "key"));
+ assertEquals(1, subject.getMetadata("subject", "key").size());
+ assertEquals(10, subject.getMetadata("subject", "key").get(0).value());
+ }
+
+ private class StringMetadataStore extends MetadataStoreBase<String> implements MetadataStore<String> {
+ @Override
+ protected String disambiguate(String subject, String metadataKey) {
+ return subject + ":" + metadataKey;
+ }
+ }
+
+ private class Counter {
+ int c = 0;
+
+ public void increment() {
+ c++;
+ }
+
+ public int value() {
+ return c;
+ }
+ }
+}
diff --git a/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java b/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java
index 52e4a0fb..ee3d8acf 100644
--- a/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java
+++ b/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java
@@ -11,8 +11,9 @@ public class StandardMessengerTest {
return new StandardMessenger();
}
+ private int count = 0;
public TestPlugin getPlugin() {
- return new TestPlugin();
+ return new TestPlugin("" + count++);
}
@Test
diff --git a/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java b/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java
index 4ecd9745..da6b7d81 100644
--- a/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java
+++ b/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java
@@ -22,6 +22,7 @@ import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.map.MapView;
+import org.bukkit.metadata.MetadataValue;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
@@ -658,4 +659,20 @@ public class TestPlayer implements Player {
public EntityType getType() {
return EntityType.PLAYER;
}
+
+ public void setMetadata(String metadataKey, MetadataValue newMetadataValue) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public List<MetadataValue> getMetadata(String metadataKey) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public boolean hasMetadata(String metadataKey) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public void removeMetadata(String metadataKey, Plugin owningPlugin) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
}
diff --git a/src/test/java/org/bukkit/plugin/messaging/TestPlugin.java b/src/test/java/org/bukkit/plugin/messaging/TestPlugin.java
index a03581c3..f14e2e75 100644
--- a/src/test/java/org/bukkit/plugin/messaging/TestPlugin.java
+++ b/src/test/java/org/bukkit/plugin/messaging/TestPlugin.java
@@ -1,8 +1,8 @@
package org.bukkit.plugin.messaging;
-import com.avaje.ebean.EbeanServer;
import java.io.File;
import java.io.InputStream;
+
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
@@ -13,19 +13,31 @@ import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginLoader;
import org.bukkit.plugin.PluginLogger;
-public class TestPlugin implements Plugin {
+import com.avaje.ebean.EbeanServer;
+
+public class TestPlugin extends Plugin {
private boolean enabled = true;
+ final private String pluginName;
+
+ public TestPlugin(String pluginName) {
+ this.pluginName = pluginName;
+ }
+
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
+ public String getName() {
+ return pluginName;
+ }
+
public File getDataFolder() {
throw new UnsupportedOperationException("Not supported.");
}
public PluginDescriptionFile getDescription() {
- throw new UnsupportedOperationException("Not supported.");
+ return new PluginDescriptionFile(pluginName, "1.0", "test.test");
}
public FileConfiguration getConfig() {
@@ -100,4 +112,19 @@ public class TestPlugin implements Plugin {
throw new UnsupportedOperationException("Not supported.");
}
+ @Override
+ public int hashCode() {
+ return getName().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ return getName().equals(((TestPlugin) obj).getName());
+ }
}