Home | History | Annotate | Download | only in impl
      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      */
     59     public static void setStrength(Strength strength) { CacheValue.strength = strength; }
     60 
     61     /**
     62      * Returns true if the "strength" is set to {@code STRONG}.
     63      */
     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")
     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      */
     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      */
     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      */
    101     public abstract V resetIfCleared(V value);
    102 
    103     private static final class NullValue<V> extends CacheValue<V> {
    104         @Override
    105         public boolean isNull() { return true; }
    106         @Override
    107         public V get() { return null; }
    108         @Override
    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 
    120         StrongValue(V value) { this.value = value; }
    121         @Override
    122         public V get() { return value; }
    123         @Override
    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 
    134         SoftValue(V value) { ref = new SoftReference<V>(value); }
    135         @Override
    136         public V get() { return ref.get(); }
    137         @Override
    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