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