1ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/*
2ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Copyright (C) 2011 The Android Open Source Project
3ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
4ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Licensed under the Apache License, Version 2.0 (the "License");
5ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * you may not use this file except in compliance with the License.
6ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * You may obtain a copy of the License at
7ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
8ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *      http://www.apache.org/licenses/LICENSE-2.0
9ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
10ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Unless required by applicable law or agreed to in writing, software
11ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * distributed under the License is distributed on an "AS IS" BASIS,
12ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * See the License for the specific language governing permissions and
14ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * limitations under the License.
15ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */
16ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
17ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianpackage com.android.dialer.util;
18ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
19ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.util.LruCache;
20ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.concurrent.atomic.AtomicInteger;
21ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport javax.annotation.concurrent.Immutable;
22ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport javax.annotation.concurrent.ThreadSafe;
23ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
24ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/**
25ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * An LRU cache in which all items can be marked as expired at a given time and it is possible to
26ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * query whether a particular cached value is expired or not.
27ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
28ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * <p>A typical use case for this is caching of values which are expensive to compute but which are
29ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * still useful when out of date.
30ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
31ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * <p>Consider a cache for contact information:
32ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
33ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * <pre>{@code
34ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * private ExpirableCache<String, Contact> mContactCache;
35ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * }</pre>
36ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
37ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * which stores the contact information for a given phone number.
38ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
39ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * <p>When we need to store contact information for a given phone number, we can look up the info in
40ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * the cache:
41ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
42ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * <pre>{@code
43ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * CachedValue<Contact> cachedContact = mContactCache.getCachedValue(phoneNumber);
44ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * }</pre>
45ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
46ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * We might also want to fetch the contact information again if the item is expired.
47ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
48ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * <pre>
49ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *     if (cachedContact.isExpired()) {
50ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *         fetchContactForNumber(phoneNumber,
51ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *                 new FetchListener() {
52ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *                     &#64;Override
53ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *                     public void onFetched(Contact contact) {
54ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *                         mContactCache.put(phoneNumber, contact);
55ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *                     }
56ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *                 });
57ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *     }</pre>
58ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
59ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * and insert it back into the cache when the fetch completes.
60ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
61ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * <p>At a certain point we want to expire the content of the cache because we know the content may
62ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * no longer be up-to-date, for instance, when resuming the activity this is shown into:
63ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
64ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * <pre>
65ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *     &#64;Override
66ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *     protected onResume() {
67ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *         // We were paused for some time, the cached value might no longer be up to date.
68ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *         mContactCache.expireAll();
69ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *         super.onResume();
70ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *     }
71ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * </pre>
72ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
73ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * The values will be still available from the cache, but they will be expired.
74ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
75ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * <p>If interested only in the value itself, not whether it is expired or not, one should use the
76ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * {@link #getPossiblyExpired(Object)} method. If interested only in non-expired values, one should
77ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * use the {@link #get(Object)} method instead.
78ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
79ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * <p>This class wraps around an {@link LruCache} instance: it follows the {@link LruCache} behavior
80ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * for evicting items when the cache is full. It is possible to supply your own subclass of LruCache
81ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * by using the {@link #create(LruCache)} method, which can define a custom expiration policy. Since
82ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * the underlying cache maps keys to cached values it can determine which items are expired and
83ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * which are not, allowing for an implementation that evicts expired items before non expired ones.
84ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
85ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * <p>This class is thread-safe.
86ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
87ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * @param <K> the type of the keys
88ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * @param <V> the type of the values
89ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */
90ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian@ThreadSafe
91ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianpublic class ExpirableCache<K, V> {
92ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
93ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
94ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * The current generation of items added to the cache.
95ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
96ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>Items in the cache can belong to a previous generation, but in that case they would be
97ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * expired.
98ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
99ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @see ExpirableCache.CachedValue#isExpired()
100ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
101183cb71663320f16149d83eeebaff7795a4b55f2linyuh  private final AtomicInteger generation;
102ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** The underlying cache used to stored the cached values. */
103183cb71663320f16149d83eeebaff7795a4b55f2linyuh  private LruCache<K, CachedValue<V>> cache;
104ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
105ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private ExpirableCache(LruCache<K, CachedValue<V>> cache) {
106183cb71663320f16149d83eeebaff7795a4b55f2linyuh    this.cache = cache;
107183cb71663320f16149d83eeebaff7795a4b55f2linyuh    generation = new AtomicInteger(0);
108ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
109ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
110ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
111ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Creates a new {@link ExpirableCache} that wraps the given {@link LruCache}.
112ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
113ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>The created cache takes ownership of the cache passed in as an argument.
114ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
115ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param <K> the type of the keys
116ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param <V> the type of the values
117ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param cache the cache to store the value in
118ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @return the newly created expirable cache
119ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @throws IllegalArgumentException if the cache is not empty
120ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
121ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public static <K, V> ExpirableCache<K, V> create(LruCache<K, CachedValue<V>> cache) {
122ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return new ExpirableCache<K, V>(cache);
123ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
124ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
125ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
126ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Creates a new {@link ExpirableCache} with the given maximum size.
127ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
128ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param <K> the type of the keys
129ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param <V> the type of the values
130ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @return the newly created expirable cache
131ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
132ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public static <K, V> ExpirableCache<K, V> create(int maxSize) {
133ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return create(new LruCache<K, CachedValue<V>>(maxSize));
134ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
135ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
136ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
137ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Returns the cached value for the given key, or null if no value exists.
138ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
139ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>The cached value gives access both to the value associated with the key and whether it is
140ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * expired or not.
141ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
142ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>If not interested in whether the value is expired, use {@link #getPossiblyExpired(Object)}
143ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * instead.
144ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
145ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>If only wants values that are not expired, use {@link #get(Object)} instead.
146ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
147ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param key the key to look up
148ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
149ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public CachedValue<V> getCachedValue(K key) {
150183cb71663320f16149d83eeebaff7795a4b55f2linyuh    return cache.get(key);
151ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
152ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
153ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
154ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Returns the value for the given key, or null if no value exists.
155ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
156ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>When using this method, it is not possible to determine whether the value is expired or not.
157ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Use {@link #getCachedValue(Object)} to achieve that instead. However, if using {@link
158ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * #getCachedValue(Object)} to determine if an item is expired, one should use the item within the
159ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * {@link CachedValue} and not call {@link #getPossiblyExpired(Object)} to get the value
160ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * afterwards, since that is not guaranteed to return the same value or that the newly returned
161ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * value is in the same state.
162ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
163ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param key the key to look up
164ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
165ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public V getPossiblyExpired(K key) {
166ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    CachedValue<V> cachedValue = getCachedValue(key);
167ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return cachedValue == null ? null : cachedValue.getValue();
168ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
169ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
170ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
171ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Returns the value for the given key only if it is not expired, or null if no value exists or is
172ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * expired.
173ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
174ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>This method will return null if either there is no value associated with this key or if the
175ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * associated value is expired.
176ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
177ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param key the key to look up
178ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
179ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public V get(K key) {
180ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    CachedValue<V> cachedValue = getCachedValue(key);
181ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return cachedValue == null || cachedValue.isExpired() ? null : cachedValue.getValue();
182ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
183ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
184ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
185ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Puts an item in the cache.
186ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
187ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>Newly added item will not be expired until {@link #expireAll()} is next called.
188ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
189ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param key the key to look up
190ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param value the value to associate with the key
191ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
192ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void put(K key, V value) {
193183cb71663320f16149d83eeebaff7795a4b55f2linyuh    cache.put(key, newCachedValue(value));
194ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
195ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
196ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
197ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Mark all items currently in the cache as expired.
198ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
199ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>Newly added items after this call will be marked as not expired.
200ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
201ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>Expiring the items in the cache does not imply they will be evicted.
202ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
203ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void expireAll() {
204183cb71663320f16149d83eeebaff7795a4b55f2linyuh    generation.incrementAndGet();
205ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
206ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
207ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
208ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Creates a new {@link CachedValue} instance to be stored in this cache.
209ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
210ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>Implementation of {@link LruCache#create(K)} can use this method to create a new entry.
211ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
212ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public CachedValue<V> newCachedValue(V value) {
213183cb71663320f16149d83eeebaff7795a4b55f2linyuh    return new GenerationalCachedValue<V>(value, generation);
214ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
215ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
216ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
217ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * A cached value stored inside the cache.
218ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
219ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>It provides access to the value stored in the cache but also allows to check whether the
220ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * value is expired.
221ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
222ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param <V> the type of value stored in the cache
223ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
224ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public interface CachedValue<V> {
225ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
226ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /** Returns the value stored in the cache for a given key. */
227ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    V getValue();
228ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
229ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /**
230ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * Checks whether the value, while still being present in the cache, is expired.
231ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     *
232ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * @return true if the value is expired
233ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     */
234ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    boolean isExpired();
235ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
236ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
237ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Cached values storing the generation at which they were added. */
238ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Immutable
239ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static class GenerationalCachedValue<V> implements ExpirableCache.CachedValue<V> {
240ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
241ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /** The value stored in the cache. */
242183cb71663320f16149d83eeebaff7795a4b55f2linyuh    public final V value;
243ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /** The generation at which the value was added to the cache. */
244183cb71663320f16149d83eeebaff7795a4b55f2linyuh    private final int generation;
245ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /** The atomic integer storing the current generation of the cache it belongs to. */
246183cb71663320f16149d83eeebaff7795a4b55f2linyuh    private final AtomicInteger cacheGeneration;
247ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
248ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /**
249ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * @param cacheGeneration the atomic integer storing the generation of the cache in which this
250ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     *     value will be stored
251ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     */
252ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public GenerationalCachedValue(V value, AtomicInteger cacheGeneration) {
253183cb71663320f16149d83eeebaff7795a4b55f2linyuh      this.value = value;
254183cb71663320f16149d83eeebaff7795a4b55f2linyuh      this.cacheGeneration = cacheGeneration;
255ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Snapshot the current generation.
256183cb71663320f16149d83eeebaff7795a4b55f2linyuh      generation = this.cacheGeneration.get();
257ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
258ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
259ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
260ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public V getValue() {
261183cb71663320f16149d83eeebaff7795a4b55f2linyuh      return value;
262ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
263ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
264ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
265ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public boolean isExpired() {
266183cb71663320f16149d83eeebaff7795a4b55f2linyuh      return generation != cacheGeneration.get();
267ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
268ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
269ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian}
270