1/*
2 * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 *******************************************************************************
28 * Copyright (C) 2009-2010, International Business Machines Corporation and    *
29 * others. All Rights Reserved.                                                *
30 *******************************************************************************
31 */
32package sun.util.locale;
33
34import java.lang.ref.ReferenceQueue;
35import java.lang.ref.SoftReference;
36import java.util.concurrent.ConcurrentHashMap;
37import java.util.concurrent.ConcurrentMap;
38
39public abstract class LocaleObjectCache<K, V> {
40    private ConcurrentMap<K, CacheEntry<K, V>> map;
41    private ReferenceQueue<V> queue = new ReferenceQueue<>();
42
43    public LocaleObjectCache() {
44        this(16, 0.75f, 16);
45    }
46
47    public LocaleObjectCache(int initialCapacity, float loadFactor, int concurrencyLevel) {
48        map = new ConcurrentHashMap<>(initialCapacity, loadFactor, concurrencyLevel);
49    }
50
51    public V get(K key) {
52        V value = null;
53
54        cleanStaleEntries();
55        CacheEntry<K, V> entry = map.get(key);
56        if (entry != null) {
57            value = entry.get();
58        }
59        if (value == null) {
60            key = normalizeKey(key);
61            V newVal = createObject(key);
62            if (key == null || newVal == null) {
63                // subclass must return non-null key/value object
64                return null;
65            }
66
67            CacheEntry<K, V> newEntry = new CacheEntry<>(key, newVal, queue);
68
69            entry = map.putIfAbsent(key, newEntry);
70            if (entry == null) {
71                value = newVal;
72            } else {
73                value = entry.get();
74                if (value == null) {
75                    map.put(key, newEntry);
76                    value = newVal;
77                }
78            }
79        }
80        return value;
81    }
82
83    protected V put(K key, V value) {
84        CacheEntry<K, V> entry = new CacheEntry<>(key, value, queue);
85        CacheEntry<K, V> oldEntry = map.put(key, entry);
86        return (oldEntry == null) ? null : oldEntry.get();
87    }
88
89    @SuppressWarnings("unchecked")
90    private void cleanStaleEntries() {
91        CacheEntry<K, V> entry;
92        while ((entry = (CacheEntry<K, V>)queue.poll()) != null) {
93            map.remove(entry.getKey());
94        }
95    }
96
97    protected abstract V createObject(K key);
98
99    protected K normalizeKey(K key) {
100        return key;
101    }
102
103    private static class CacheEntry<K, V> extends SoftReference<V> {
104        private K key;
105
106        CacheEntry(K key, V value, ReferenceQueue<V> queue) {
107            super(value, queue);
108            this.key = key;
109        }
110
111        K getKey() {
112            return key;
113        }
114    }
115}
116