1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html#License 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2016, International Business Machines Corporation and 6 * others. All Rights Reserved. 7 ******************************************************************************* 8 */ 9 package com.ibm.icu.impl; 10 11 import java.lang.ref.Reference; 12 import java.lang.ref.SoftReference; 13 14 import com.ibm.icu.util.ICUException; 15 16 /** 17 * Value type for cache items: 18 * Holds a value either via a direct reference or via a {@link Reference}, 19 * depending on the current "strength" when {@code getInstance()} was called. 20 * 21 * <p>The value is <i>conceptually<i> immutable. 22 * If it is held via a direct reference, then it is actually immutable. 23 * 24 * <p>A {@code Reference} may be cleared (garbage-collected), 25 * after which {@code get()} returns null. 26 * It can then be reset via {@code resetIfAbsent()}. 27 * The new value should be the same as, or equivalent to, the old value. 28 * 29 * <p>Null values are supported. They can be distinguished from cleared values 30 * via {@code isNull()}. 31 * 32 * @param <V> Cache instance value type 33 */ 34 public abstract class CacheValue<V> { 35 /** 36 * "Strength" of holding a value in CacheValue instances. 37 * The default strength is {@code SOFT}. 38 */ 39 public enum Strength { 40 /** 41 * Subsequent {@code getInstance()}-created objects 42 * will hold direct references to their values. 43 */ 44 STRONG, 45 /** 46 * Subsequent {@code getInstance()}-created objects 47 * will hold {@link SoftReference}s to their values. 48 */ 49 SOFT 50 }; 51 private static volatile Strength strength = Strength.SOFT; 52 53 @SuppressWarnings("rawtypes") 54 private static final CacheValue NULL_VALUE = new NullValue(); 55 56 /** 57 * Changes the "strength" of value references for subsequent {@code getInstance()} calls. 58 */ setStrength(Strength strength)59 public static void setStrength(Strength strength) { CacheValue.strength = strength; } 60 61 /** 62 * Returns true if the "strength" is set to {@code STRONG}. 63 */ futureInstancesWillBeStrong()64 public static boolean futureInstancesWillBeStrong() { return strength == Strength.STRONG; } 65 66 /** 67 * Returns a CacheValue instance that holds the value. 68 * It holds it directly if the value is null or if the current "strength" is {@code STRONG}. 69 * Otherwise, it holds it via a {@link Reference}. 70 */ 71 @SuppressWarnings("unchecked") getInstance(V value)72 public static <V> CacheValue<V> getInstance(V value) { 73 if (value == null) { 74 return NULL_VALUE; 75 } 76 return strength == Strength.STRONG ? new StrongValue<V>(value) : new SoftValue<V>(value); 77 } 78 79 /** 80 * Distinguishes a null value from a Reference value that has been cleared. 81 * 82 * @return true if this object represents a null value. 83 */ isNull()84 public boolean isNull() { return false; } 85 /** 86 * Returns the value (which can be null), 87 * or null if it was held in a Reference and has been cleared. 88 */ get()89 public abstract V get(); 90 /** 91 * If the value was held via a {@link Reference} which has been cleared, 92 * then it is replaced with a new {@link Reference} to the new value, 93 * and the new value is returned. 94 * The old and new values should be the same or equivalent. 95 * 96 * <p>Otherwise the old value is returned. 97 * 98 * @param value Replacement value, for when the current {@link Reference} has been cleared. 99 * @return The old or new value. 100 */ resetIfCleared(V value)101 public abstract V resetIfCleared(V value); 102 103 private static final class NullValue<V> extends CacheValue<V> { 104 @Override isNull()105 public boolean isNull() { return true; } 106 @Override get()107 public V get() { return null; } 108 @Override resetIfCleared(V value)109 public V resetIfCleared(V value) { 110 if (value != null) { 111 throw new ICUException("resetting a null value to a non-null value"); 112 } 113 return null; 114 } 115 } 116 117 private static final class StrongValue<V> extends CacheValue<V> { 118 private V value; 119 StrongValue(V value)120 StrongValue(V value) { this.value = value; } 121 @Override get()122 public V get() { return value; } 123 @Override resetIfCleared(V value)124 public V resetIfCleared(V value) { 125 // value and this.value should be equivalent, but 126 // we do not require equals() to be implemented appropriately. 127 return this.value; 128 } 129 } 130 131 private static final class SoftValue<V> extends CacheValue<V> { 132 private volatile Reference<V> ref; // volatile for unsynchronized get() 133 SoftValue(V value)134 SoftValue(V value) { ref = new SoftReference<V>(value); } 135 @Override get()136 public V get() { return ref.get(); } 137 @Override resetIfCleared(V value)138 public synchronized V resetIfCleared(V value) { 139 V oldValue = ref.get(); 140 if (oldValue == null) { 141 ref = new SoftReference<V>(value); 142 return value; 143 } else { 144 // value and oldValue should be equivalent, but 145 // we do not require equals() to be implemented appropriately. 146 return oldValue; 147 } 148 } 149 } 150 } 151