summaryrefslogtreecommitdiffstats
path: root/android_icu4j/src/main/java/android/icu/impl/CacheValue.java
blob: 88850c15820f4a967ff7c86329d5fc3659e8c7ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* GENERATED SOURCE. DO NOT MODIFY. */
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
/*
 *******************************************************************************
 * Copyright (C) 2016, International Business Machines Corporation and
 * others. All Rights Reserved.
 *******************************************************************************
 */
package android.icu.impl;

import java.lang.ref.Reference;
import java.lang.ref.SoftReference;

import android.icu.util.ICUException;

/**
 * Value type for cache items:
 * Holds a value either via a direct reference or via a {@link Reference},
 * depending on the current "strength" when {@code getInstance()} was called.
 *
 * <p>The value is <i>conceptually<i> immutable.
 * If it is held via a direct reference, then it is actually immutable.
 *
 * <p>A {@code Reference} may be cleared (garbage-collected),
 * after which {@code get()} returns null.
 * It can then be reset via {@code resetIfAbsent()}.
 * The new value should be the same as, or equivalent to, the old value.
 *
 * <p>Null values are supported. They can be distinguished from cleared values
 * via {@code isNull()}.
 *
 * @param <V> Cache instance value type
 * @hide Only a subset of ICU is exposed in Android
 */
public abstract class CacheValue<V> {
    /**
     * "Strength" of holding a value in CacheValue instances.
     * The default strength is {@code SOFT}.
     * @hide Only a subset of ICU is exposed in Android
     */
    public enum Strength {
        /**
         * Subsequent {@code getInstance()}-created objects
         * will hold direct references to their values.
         */
        STRONG,
        /**
         * Subsequent {@code getInstance()}-created objects
         * will hold {@link SoftReference}s to their values.
         */
        SOFT
    };
    private static volatile Strength strength = Strength.SOFT;

    @SuppressWarnings("rawtypes")
    private static final CacheValue NULL_VALUE = new NullValue();

    /**
     * Changes the "strength" of value references for subsequent {@code getInstance()} calls.
     */
    public static void setStrength(Strength strength) { CacheValue.strength = strength; }

    /**
     * Returns true if the "strength" is set to {@code STRONG}.
     */
    public static boolean futureInstancesWillBeStrong() { return strength == Strength.STRONG; }

    /**
     * Returns a CacheValue instance that holds the value.
     * It holds it directly if the value is null or if the current "strength" is {@code STRONG}.
     * Otherwise, it holds it via a {@link Reference}.
     */
    @SuppressWarnings("unchecked")
    public static <V> CacheValue<V> getInstance(V value) {
        if (value == null) {
            return NULL_VALUE;
        }
        return strength == Strength.STRONG ? new StrongValue<V>(value) : new SoftValue<V>(value);
    }

    /**
     * Distinguishes a null value from a Reference value that has been cleared.
     *
     * @return true if this object represents a null value.
     */
    public boolean isNull() { return false; }
    /**
     * Returns the value (which can be null),
     * or null if it was held in a Reference and has been cleared.
     */
    public abstract V get();
    /**
     * If the value was held via a {@link Reference} which has been cleared,
     * then it is replaced with a new {@link Reference} to the new value,
     * and the new value is returned.
     * The old and new values should be the same or equivalent.
     *
     * <p>Otherwise the old value is returned.
     *
     * @param value Replacement value, for when the current {@link Reference} has been cleared.
     * @return The old or new value.
     */
    public abstract V resetIfCleared(V value);

    private static final class NullValue<V> extends CacheValue<V> {
        @Override
        public boolean isNull() { return true; }
        @Override
        public V get() { return null; }
        @Override
        public V resetIfCleared(V value) {
            if (value != null) {
                throw new ICUException("resetting a null value to a non-null value");
            }
            return null;
        }
    }

    private static final class StrongValue<V> extends CacheValue<V> {
        private V value;

        StrongValue(V value) { this.value = value; }
        @Override
        public V get() { return value; }
        @Override
        public V resetIfCleared(V value) {
            // value and this.value should be equivalent, but
            // we do not require equals() to be implemented appropriately.
            return this.value;
        }
    }

    private static final class SoftValue<V> extends CacheValue<V> {
        private volatile Reference<V> ref;  // volatile for unsynchronized get()

        SoftValue(V value) { ref = new SoftReference<V>(value); }
        @Override
        public V get() { return ref.get(); }
        @Override
        public synchronized V resetIfCleared(V value) {
            V oldValue = ref.get();
            if (oldValue == null) {
                ref = new SoftReference<V>(value);
                return value;
            } else {
                // value and oldValue should be equivalent, but
                // we do not require equals() to be implemented appropriately.
                return oldValue;
            }
        }
    }
}