Bukkit/src/main/java/org/bukkit/metadata/LazyMetadataValue.java
rmichela cd732ee3f7 [Bleeding] Added a Metadata framework for Entities, Blocks, and Worlds
This metadata implementation has the following features:

- All metadata is lazy. Metadata values are not actually computed until another plugin requests them. Memory and CPU are conserved by not computing and storing unnecessary metadata values.

- All metadata is cached. Once a metadata value is computed its value is cached in the metadata store to prevent further unnecessary computation. An invalidation mechanism is provided to flush the cache and force recompilation of metadata values.

- All metadata is stored in basic data types. Convenience methods in the MetadataValue class allow for the conversion of metadata data types when possible. Restricting metadata to basic data types prevents the accidental linking of large object graphs into metadata. Metadata is persistent across the lifetime of the application and adding large object graphs would damage garbage collector performance.

- Metadata access is thread safe. Care has been taken to protect the internal data structures and access them in a thread safe manner.

- Metadata is exposed for all objects that descend from Entity, Block, and World. All Entity and World metadata is stored at the Server  level and all Block metadata is stored at the World level.

- Metadata is NOT keyed on references to original objects - instead metadata is keyed off of unique fields within those objects. Doing this allows metadata to exist for blocks that are in chunks not currently in memory. Additionally, Player objects are keyed off of player name so that Player metadata remains consistent between logins.

- Metadata convenience methods have been added to all Entities, Players, Blocks, BlockStates, and World allowing direct access to an individual instance's metadata.

- Players and OfflinePlayers share a single metadata store, allowing player metadata to be manipulated regardless of the player's current online status.
2012-02-29 19:16:04 +01:00

159 lines
4.9 KiB
Java

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
}
}