From 63128012244d05ba1d94cd83febb0faf354cfac4 Mon Sep 17 00:00:00 2001 From: crast Date: Sat, 16 Feb 2013 14:33:24 -0700 Subject: Refactor common metadata code into base class. Fixes BUKKIT-3624 Implementing the MetadataValue interface is significant work due to having to provide a large amount of conversion stub methods. This commit adds a new optional abstract base class to aid in implementation. --- .../org/bukkit/metadata/LazyMetadataValue.java | 61 +------------- .../org/bukkit/metadata/MetadataValueAdapter.java | 77 +++++++++++++++++ .../bukkit/metadata/MetadataValueAdapterTest.java | 97 ++++++++++++++++++++++ 3 files changed, 176 insertions(+), 59 deletions(-) create mode 100644 src/main/java/org/bukkit/metadata/MetadataValueAdapter.java create mode 100644 src/test/java/org/bukkit/metadata/MetadataValueAdapterTest.java diff --git a/src/main/java/org/bukkit/metadata/LazyMetadataValue.java b/src/main/java/org/bukkit/metadata/LazyMetadataValue.java index cc0ba506..57fdc502 100644 --- a/src/main/java/org/bukkit/metadata/LazyMetadataValue.java +++ b/src/main/java/org/bukkit/metadata/LazyMetadataValue.java @@ -5,7 +5,6 @@ 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. @@ -14,11 +13,10 @@ import org.bukkit.util.NumberConversions; * or invalidated at the individual or plugin level. Once invalidated, the LazyMetadataValue will recompute its value * when asked. */ -public class LazyMetadataValue implements MetadataValue { +public class LazyMetadataValue extends MetadataValueAdapter implements MetadataValue { private Callable lazyValue; private CacheStrategy cacheStrategy; private SoftReference internalValue = new SoftReference(null); - private Plugin owningPlugin; private static final Object ACTUALLY_NULL = new Object(); /** @@ -39,19 +37,14 @@ public class LazyMetadataValue implements MetadataValue { * @param lazyValue the lazy value assigned to this metadata value. */ public LazyMetadataValue(Plugin owningPlugin, CacheStrategy cacheStrategy, Callable lazyValue) { - Validate.notNull(owningPlugin, "owningPlugin cannot be null"); + super(owningPlugin); 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(); @@ -61,56 +54,6 @@ public class LazyMetadataValue implements MetadataValue { 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. * diff --git a/src/main/java/org/bukkit/metadata/MetadataValueAdapter.java b/src/main/java/org/bukkit/metadata/MetadataValueAdapter.java new file mode 100644 index 00000000..c4b8b392 --- /dev/null +++ b/src/main/java/org/bukkit/metadata/MetadataValueAdapter.java @@ -0,0 +1,77 @@ +package org.bukkit.metadata; + +import org.apache.commons.lang.Validate; +import org.bukkit.plugin.Plugin; +import org.bukkit.util.NumberConversions; + +/** + * Optional base class for facilitating MetadataValue implementations. + * + * This provides all the conversion functions for MetadataValue + * so that writing an implementation of MetadataValue is as simple + * as implementing value() and invalidate(). + * + */ +public abstract class MetadataValueAdapter implements MetadataValue { + protected final Plugin owningPlugin; + + protected MetadataValueAdapter(Plugin owningPlugin) { + Validate.notNull(owningPlugin, "owningPlugin cannot be null"); + this.owningPlugin = owningPlugin; + } + + public Plugin getOwningPlugin() { + return owningPlugin; + } + + 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(); + } + +} diff --git a/src/test/java/org/bukkit/metadata/MetadataValueAdapterTest.java b/src/test/java/org/bukkit/metadata/MetadataValueAdapterTest.java new file mode 100644 index 00000000..7d8a17fe --- /dev/null +++ b/src/test/java/org/bukkit/metadata/MetadataValueAdapterTest.java @@ -0,0 +1,97 @@ +package org.bukkit.metadata; + +import static org.junit.Assert.assertEquals; + +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.TestPlugin; +import org.junit.Test; + +public class MetadataValueAdapterTest { + private TestPlugin plugin = new TestPlugin("x"); + + @Test + public void testAdapterBasics() { + IncrementingMetaValue mv = new IncrementingMetaValue(plugin); + // check getOwningPlugin + assertEquals(mv.getOwningPlugin(), this.plugin); + + // Check value-getting and invalidation. + assertEquals(new Integer(1), mv.value()); + assertEquals(new Integer(2), mv.value()); + mv.invalidate(); + assertEquals(new Integer(1), mv.value()); + } + + @Test + public void testAdapterConversions() { + IncrementingMetaValue mv = new IncrementingMetaValue(plugin); + + assertEquals(1, mv.asInt()); + assertEquals(2L, mv.asLong()); + assertEquals(3.0, mv.asFloat(), 0.001); + assertEquals(4, mv.asByte()); + assertEquals(5.0, mv.asDouble(), 0.001); + assertEquals(6, mv.asShort()); + assertEquals("7", mv.asString()); + } + + /** Boolean conversion is non-trivial, we want to test it thoroughly. */ + @Test + public void testBooleanConversion() { + // null is False. + assertEquals(false, simpleValue(null).asBoolean()); + + // String to boolean. + assertEquals(true, simpleValue("True").asBoolean()); + assertEquals(true, simpleValue("TRUE").asBoolean()); + assertEquals(false, simpleValue("false").asBoolean()); + + // Number to boolean. + assertEquals(true, simpleValue(1).asBoolean()); + assertEquals(true, simpleValue(5.0).asBoolean()); + assertEquals(false, simpleValue(0).asBoolean()); + assertEquals(false, simpleValue(0.1).asBoolean()); + + // Boolean as boolean, of course. + assertEquals(true, simpleValue(Boolean.TRUE).asBoolean()); + assertEquals(false, simpleValue(Boolean.FALSE).asBoolean()); + + // any object that is not null and not a Boolean, String, or Number is true. + assertEquals(true, simpleValue(new Object()).asBoolean()); + } + + /** Test String conversions return an empty string when given null. */ + @Test + public void testStringConversionNull() { + assertEquals("", simpleValue(null).asString()); + } + + /** Get a fixed value MetadataValue. */ + private MetadataValue simpleValue(Object value) { + return new FixedMetadataValue(plugin, value); + } + + /** + * A sample non-trivial MetadataValueAdapter implementation. + * + * The rationale for implementing an incrementing value is to have a value + * which changes with every call to value(). This is important for testing + * because we want to make sure all the tested conversions are calling the + * value() method exactly once and no caching is going on. + */ + class IncrementingMetaValue extends MetadataValueAdapter { + private int internalValue = 0; + + protected IncrementingMetaValue(Plugin owningPlugin) { + super(owningPlugin); + } + + public Object value() { + return ++internalValue; + } + + public void invalidate() { + internalValue = 0; + } + } +} -- cgit v1.2.3