1/* GENERATED SOURCE. DO NOT MODIFY. */
2// © 2016 and later: Unicode, Inc. and others.
3// License & terms of use: http://www.unicode.org/copyright.html#License
4/*
5*******************************************************************************
6*   Copyright (C) 2010-2016, International Business Machines
7*   Corporation and others.  All Rights Reserved.
8*******************************************************************************
9*/
10package android.icu.impl;
11
12import java.util.concurrent.ConcurrentHashMap;
13
14/**
15 * Generic, thread-safe cache implementation, usually storing cached instances
16 * in {@link java.lang.ref.Reference}s via {@link CacheValue}s.
17 * To use, instantiate a subclass which implements the createInstance() method,
18 * and call get() with the key and the data. The get() call will use the data
19 * only if it needs to call createInstance(), otherwise the data is ignored.
20 *
21 * <p>When caching instances while the CacheValue "strength" is {@code SOFT},
22 * the Java runtime can later release these instances once they are not used any more at all.
23 * If such an instance is then requested again,
24 * the getInstance() method will call createInstance() again and reset the CacheValue.
25 * The cache holds on to its map of keys to CacheValues forever.
26 *
27 * <p>A value can be null if createInstance() returns null.
28 * In this case, it must do so consistently for the same key and data.
29 *
30 * @param <K> Cache lookup key type
31 * @param <V> Cache instance value type (must not be a CacheValue)
32 * @param <D> Data type for creating a new instance value
33 *
34 * @author Markus Scherer, Mark Davis
35 * @hide Only a subset of ICU is exposed in Android
36 */
37public abstract class SoftCache<K, V, D> extends CacheBase<K, V, D> {
38    private ConcurrentHashMap<K, Object> map = new ConcurrentHashMap<K, Object>();
39
40    @SuppressWarnings("unchecked")
41    @Override
42    public final V getInstance(K key, D data) {
43        // We synchronize twice, once in the ConcurrentHashMap and
44        // once in valueRef.resetIfCleared(value),
45        // because we prefer the fine-granularity locking of the ConcurrentHashMap
46        // over coarser locking on the whole cache instance.
47        // We use a CacheValue (a second level of indirection) because
48        // ConcurrentHashMap.putIfAbsent() never replaces the key's value, and if it were
49        // a simple Reference we would not be able to reset its value after it has been cleared.
50        // (And ConcurrentHashMap.put() always replaces the value, which we don't want either.)
51        Object mapValue = map.get(key);
52        if(mapValue != null) {
53            if(!(mapValue instanceof CacheValue)) {
54                // The value was stored directly.
55                return (V)mapValue;
56            }
57            CacheValue<V> cv = (CacheValue<V>)mapValue;
58            if(cv.isNull()) {
59                return null;
60            }
61            V value = cv.get();
62            if(value != null) {
63                return value;
64            }
65            // The instance has been evicted, its Reference cleared.
66            // Create and set a new instance.
67            value = createInstance(key, data);
68            return cv.resetIfCleared(value);
69        } else /* valueRef == null */ {
70            // We had never cached an instance for this key.
71            V value = createInstance(key, data);
72            mapValue = (value != null && CacheValue.futureInstancesWillBeStrong()) ?
73                    value : CacheValue.getInstance(value);
74            mapValue = map.putIfAbsent(key, mapValue);
75            if(mapValue == null) {
76                // Normal "put": Our new value is now cached.
77                return value;
78            }
79            // Race condition: Another thread beat us to putting a CacheValue
80            // into the map. Return its value, but just in case the garbage collector
81            // was aggressive, we also offer our new instance for caching.
82            if(!(mapValue instanceof CacheValue)) {
83                // The value was stored directly.
84                return (V)mapValue;
85            }
86            CacheValue<V> cv = (CacheValue<V>)mapValue;
87            return cv.resetIfCleared(value);
88        }
89    }
90}
91