1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.mms.util;
18
19import java.lang.ref.SoftReference;
20import java.util.LinkedHashMap;
21import java.util.Map;
22
23/**
24 * A simple cache with the option of using {@link SoftReference SoftReferences} to play well with
25 * the garbage collector and an LRU cache eviction algorithm to limit the number
26 * of {@link SoftReference SoftReferences}.
27 * <p>
28 * The interface of this class is a subset of {@link Map}.
29 *
30 * from Peter Balwin and books app.
31 */
32public class SimpleCache<K, V> {
33
34    /**
35     * A simple LRU cache to prevent the number of {@link Map.Entry} instances
36     * from growing infinitely.
37     */
38    @SuppressWarnings("serial")
39    private class SoftReferenceMap extends LinkedHashMap<K, SoftReference<V>> {
40
41        private final int mMaxCapacity;
42
43        public SoftReferenceMap(int initialCapacity, int maxCapacity, float loadFactor) {
44            super(initialCapacity, loadFactor, true);
45            mMaxCapacity = maxCapacity;
46        }
47
48        @Override
49        protected boolean removeEldestEntry(Map.Entry<K, SoftReference<V>> eldest) {
50            return size() > mMaxCapacity;
51        }
52    }
53
54    @SuppressWarnings("serial")
55    private class HardReferenceMap extends LinkedHashMap<K, V> {
56
57        private final int mMaxCapacity;
58
59        public HardReferenceMap(int initialCapacity, int maxCapacity, float loadFactor) {
60            super(initialCapacity, loadFactor, true);
61            mMaxCapacity = maxCapacity;
62        }
63
64        @Override
65        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
66            return size() > mMaxCapacity;
67        }
68    }
69
70    private static <V> V unwrap(SoftReference<V> ref) {
71        return ref != null ? ref.get() : null;
72    }
73
74    private final SoftReferenceMap mSoftReferences;
75    private final HardReferenceMap mHardReferences;
76
77    /**
78     * Constructor.
79     *
80     * @param initialCapacity the initial capacity for the cache.
81     * @param maxCapacity the maximum capacity for the
82     *            cache (this value may be large if soft references are used because
83     *            {@link SoftReference SoftReferences} don't consume much memory compared to the
84     *            larger data they typically contain).
85     * @param loadFactor the initial load balancing factor for the internal
86     *            {@link LinkedHashMap}
87     */
88    public SimpleCache(int initialCapacity, int maxCapacity, float loadFactor,
89            boolean useHardReferences) {
90        if (useHardReferences) {
91            mSoftReferences = null;
92            mHardReferences = new HardReferenceMap(initialCapacity, maxCapacity, loadFactor);
93        } else {
94            mSoftReferences = new SoftReferenceMap(initialCapacity, maxCapacity, loadFactor);
95            mHardReferences = null;
96        }
97    }
98
99    /**
100     * See {@link Map#get(Object)}.
101     */
102    public V get(Object key) {
103        return mSoftReferences != null ? unwrap(mSoftReferences.get(key))
104                : mHardReferences.get(key);
105    }
106
107    /**
108     * See {@link Map#put(Object, Object)}.
109     */
110    public V put(K key, V value) {
111        return mSoftReferences != null ?
112                unwrap(mSoftReferences.put(key, new SoftReference<V>(value)))
113                : mHardReferences.put(key, value);
114    }
115
116    /**
117     * See {@link Map#clear()}.
118     */
119    public void clear() {
120        if (mSoftReferences != null) {
121            mSoftReferences.clear();
122        } else {
123            mHardReferences.clear();
124        }
125    }
126
127    /**
128     * See {@link Map#remove(Object)}.
129     */
130    public V remove(K key) {
131        if (mSoftReferences != null) {
132            return unwrap(mSoftReferences.remove(key));
133        } else {
134            return mHardReferences.remove(key);
135        }
136    }
137
138}
139