001 package org.bukkit.metadata; 002 003 import java.lang.ref.SoftReference; 004 import java.util.concurrent.Callable; 005 006 import org.apache.commons.lang.Validate; 007 import org.bukkit.plugin.Plugin; 008 009 /** 010 * The LazyMetadataValue class implements a type of metadata that is not 011 * computed until another plugin asks for it. 012 * <p> 013 * By making metadata values lazy, no computation is done by the providing 014 * plugin until absolutely necessary (if ever). Additionally, 015 * LazyMetadataValue objects cache their values internally unless overridden 016 * by a {@link CacheStrategy} or invalidated at the individual or plugin 017 * level. Once invalidated, the LazyMetadataValue will recompute its value 018 * when asked. 019 */ 020 public class LazyMetadataValue extends MetadataValueAdapter implements MetadataValue { 021 private Callable<Object> lazyValue; 022 private CacheStrategy cacheStrategy; 023 private SoftReference<Object> internalValue; 024 private static final Object ACTUALLY_NULL = new Object(); 025 026 /** 027 * Initialized a LazyMetadataValue object with the default 028 * CACHE_AFTER_FIRST_EVAL cache strategy. 029 * 030 * @param owningPlugin the {@link Plugin} that created this metadata 031 * value. 032 * @param lazyValue the lazy value assigned to this metadata value. 033 */ 034 public LazyMetadataValue(Plugin owningPlugin, Callable<Object> lazyValue) { 035 this(owningPlugin, CacheStrategy.CACHE_AFTER_FIRST_EVAL, lazyValue); 036 } 037 038 /** 039 * Initializes a LazyMetadataValue object with a specific cache strategy. 040 * 041 * @param owningPlugin the {@link Plugin} that created this metadata 042 * value. 043 * @param cacheStrategy determines the rules for caching this metadata 044 * value. 045 * @param lazyValue the lazy value assigned to this metadata value. 046 */ 047 public LazyMetadataValue(Plugin owningPlugin, CacheStrategy cacheStrategy, Callable<Object> lazyValue) { 048 super(owningPlugin); 049 Validate.notNull(cacheStrategy, "cacheStrategy cannot be null"); 050 Validate.notNull(lazyValue, "lazyValue cannot be null"); 051 this.internalValue = new SoftReference<Object>(null); 052 this.lazyValue = lazyValue; 053 this.cacheStrategy = cacheStrategy; 054 } 055 056 /** 057 * Protected special constructor used by FixedMetadataValue to bypass 058 * standard setup. 059 */ 060 protected LazyMetadataValue(Plugin owningPlugin) { 061 super(owningPlugin); 062 } 063 064 public Object value() { 065 eval(); 066 Object value = internalValue.get(); 067 if (value == ACTUALLY_NULL) { 068 return null; 069 } 070 return value; 071 } 072 073 /** 074 * Lazily evaluates the value of this metadata item. 075 * 076 * @throws MetadataEvaluationException if computing the metadata value 077 * fails. 078 */ 079 private synchronized void eval() throws MetadataEvaluationException { 080 if (cacheStrategy == CacheStrategy.NEVER_CACHE || internalValue.get() == null) { 081 try { 082 Object value = lazyValue.call(); 083 if (value == null) { 084 value = ACTUALLY_NULL; 085 } 086 internalValue = new SoftReference<Object>(value); 087 } catch (Exception e) { 088 throw new MetadataEvaluationException(e); 089 } 090 } 091 } 092 093 public synchronized void invalidate() { 094 if (cacheStrategy != CacheStrategy.CACHE_ETERNALLY) { 095 internalValue.clear(); 096 } 097 } 098 099 /** 100 * Describes possible caching strategies for metadata. 101 */ 102 public enum CacheStrategy { 103 /** 104 * Once the metadata value has been evaluated, do not re-evaluate the 105 * value until it is manually invalidated. 106 */ 107 CACHE_AFTER_FIRST_EVAL, 108 109 /** 110 * Re-evaluate the metadata item every time it is requested 111 */ 112 NEVER_CACHE, 113 114 /** 115 * Once the metadata value has been evaluated, do not re-evaluate the 116 * value in spite of manual invalidation. 117 */ 118 CACHE_ETERNALLY 119 } 120 }