1 /*
2  * Copyright (C) 2010 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 
17 package com.android.quicksearchbox.util;
18 
19 import android.util.Log;
20 
21 import java.util.ArrayList;
22 import java.util.List;
23 
24 /**
25  * Abstract base class for a one-place cache that holds a value that is produced
26  * asynchronously.
27  *
28  * @param <A> The type of the data held in the cache.
29  */
30 public abstract class CachedLater<A> implements NowOrLater<A> {
31 
32     private static final String TAG = "QSB.AsyncCache";
33     private static final boolean DBG = false;
34 
35     private final Object mLock = new Object();
36 
37     private A mValue;
38 
39     private boolean mCreating;
40     private boolean mValid;
41 
42     private List<Consumer<? super A>> mWaitingConsumers;
43 
44     /**
45      * Creates the object to store in the cache. This method must call
46      * {@link #store} when it's done.
47      * This method must not block.
48      */
create()49     protected abstract void create();
50 
51     /**
52      * Saves a new value to the cache.
53      */
store(A value)54     protected void store(A value) {
55         if (DBG) Log.d(TAG, "store()");
56         List<Consumer<? super A>> waitingConsumers;
57         synchronized (mLock) {
58             mValue = value;
59             mValid = true;
60             mCreating = false;
61             waitingConsumers = mWaitingConsumers;
62             mWaitingConsumers = null;
63         }
64         if (waitingConsumers != null) {
65             for (Consumer<? super A> consumer : waitingConsumers) {
66                 if (DBG) Log.d(TAG, "Calling consumer: " + consumer);
67                 consumer.consume(value);
68             }
69         }
70     }
71 
72     /**
73      * Gets the value.
74      *
75      * @param consumer A consumer that will be given the cached value.
76      *        The consumer may be called synchronously, or asynchronously on
77      *        an unspecified thread.
78      */
getLater(Consumer<? super A> consumer)79     public void getLater(Consumer<? super A> consumer) {
80         if (DBG) Log.d(TAG, "getLater()");
81         boolean valid;
82         A value;
83         synchronized (mLock) {
84             valid = mValid;
85             value = mValue;
86             if (!valid) {
87                 if (mWaitingConsumers == null) {
88                     mWaitingConsumers = new ArrayList<Consumer<? super A>>();
89                 }
90                 mWaitingConsumers.add(consumer);
91             }
92         }
93         if (valid) {
94             if (DBG) Log.d(TAG, "valid, calling consumer synchronously");
95             consumer.consume(value);
96         } else {
97             boolean create = false;
98             synchronized (mLock) {
99                 if (!mCreating) {
100                     mCreating = true;
101                     create = true;
102                 }
103             }
104             if (create) {
105                 if (DBG) Log.d(TAG, "not valid, calling create()");
106                 create();
107             } else {
108                 if (DBG) Log.d(TAG, "not valid, already creating");
109             }
110         }
111     }
112 
113     /**
114      * Clears the cache.
115      */
clear()116     public void clear() {
117         if (DBG) Log.d(TAG, "clear()");
118         synchronized (mLock) {
119             mValue = null;
120             mValid = false;
121         }
122     }
123 
haveNow()124     public boolean haveNow() {
125         synchronized (mLock) {
126             return mValid;
127         }
128     }
129 
getNow()130     public synchronized A getNow() {
131         synchronized (mLock) {
132             if (!haveNow()) {
133                 throw new IllegalStateException("getNow() called when haveNow() is false");
134             }
135             return mValue;
136         }
137     }
138 
139 }
140