1 | // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | package org.chromium.base.test.util; |
6 | |
7 | import android.content.SharedPreferences; |
8 | |
9 | import java.util.Collections; |
10 | import java.util.HashMap; |
11 | import java.util.Map; |
12 | import java.util.Set; |
13 | |
14 | /** |
15 | * An implementation of SharedPreferences that can be used in tests. |
16 | * <p/> |
17 | * It keeps all state in memory, and there is no difference between apply() and commit(). |
18 | */ |
19 | public class InMemorySharedPreferences implements SharedPreferences { |
20 | |
21 | // Guarded on its own monitor. |
22 | private final Map<String, Object> mData; |
23 | |
24 | public InMemorySharedPreferences() { |
25 | mData = new HashMap<String, Object>(); |
26 | } |
27 | |
28 | public InMemorySharedPreferences(Map<String, Object> data) { |
29 | mData = data; |
30 | } |
31 | |
32 | @Override |
33 | public Map<String, ?> getAll() { |
34 | synchronized (mData) { |
35 | return Collections.unmodifiableMap(mData); |
36 | } |
37 | } |
38 | |
39 | @Override |
40 | public String getString(String key, String defValue) { |
41 | synchronized (mData) { |
42 | if (mData.containsKey(key)) { |
43 | return (String) mData.get(key); |
44 | } |
45 | } |
46 | return defValue; |
47 | } |
48 | |
49 | @SuppressWarnings("unchecked") |
50 | @Override |
51 | public Set<String> getStringSet(String key, Set<String> defValues) { |
52 | synchronized (mData) { |
53 | if (mData.containsKey(key)) { |
54 | return Collections.unmodifiableSet((Set<String>) mData.get(key)); |
55 | } |
56 | } |
57 | return defValues; |
58 | } |
59 | |
60 | @Override |
61 | public int getInt(String key, int defValue) { |
62 | synchronized (mData) { |
63 | if (mData.containsKey(key)) { |
64 | return (Integer) mData.get(key); |
65 | } |
66 | } |
67 | return defValue; |
68 | } |
69 | |
70 | @Override |
71 | public long getLong(String key, long defValue) { |
72 | synchronized (mData) { |
73 | if (mData.containsKey(key)) { |
74 | return (Long) mData.get(key); |
75 | } |
76 | } |
77 | return defValue; |
78 | } |
79 | |
80 | @Override |
81 | public float getFloat(String key, float defValue) { |
82 | synchronized (mData) { |
83 | if (mData.containsKey(key)) { |
84 | return (Float) mData.get(key); |
85 | } |
86 | } |
87 | return defValue; |
88 | } |
89 | |
90 | @Override |
91 | public boolean getBoolean(String key, boolean defValue) { |
92 | synchronized (mData) { |
93 | if (mData.containsKey(key)) { |
94 | return (Boolean) mData.get(key); |
95 | } |
96 | } |
97 | return defValue; |
98 | } |
99 | |
100 | @Override |
101 | public boolean contains(String key) { |
102 | synchronized (mData) { |
103 | return mData.containsKey(key); |
104 | } |
105 | } |
106 | |
107 | @Override |
108 | public SharedPreferences.Editor edit() { |
109 | return new InMemoryEditor(); |
110 | } |
111 | |
112 | @Override |
113 | public void registerOnSharedPreferenceChangeListener( |
114 | SharedPreferences.OnSharedPreferenceChangeListener |
115 | listener) { |
116 | throw new UnsupportedOperationException(); |
117 | } |
118 | |
119 | @Override |
120 | public void unregisterOnSharedPreferenceChangeListener( |
121 | SharedPreferences.OnSharedPreferenceChangeListener listener) { |
122 | throw new UnsupportedOperationException(); |
123 | } |
124 | |
125 | private class InMemoryEditor implements SharedPreferences.Editor { |
126 | |
127 | // All guarded by |mChanges| |
128 | private boolean mClearCalled; |
129 | private volatile boolean mApplyCalled; |
130 | private final Map<String, Object> mChanges = new HashMap<String, Object>(); |
131 | |
132 | @Override |
133 | public SharedPreferences.Editor putString(String key, String value) { |
134 | synchronized (mChanges) { |
135 | if (mApplyCalled) throw new IllegalStateException(); |
136 | mChanges.put(key, value); |
137 | return this; |
138 | } |
139 | } |
140 | |
141 | @Override |
142 | public SharedPreferences.Editor putStringSet(String key, Set<String> values) { |
143 | synchronized (mChanges) { |
144 | if (mApplyCalled) throw new IllegalStateException(); |
145 | mChanges.put(key, values); |
146 | return this; |
147 | } |
148 | } |
149 | |
150 | @Override |
151 | public SharedPreferences.Editor putInt(String key, int value) { |
152 | synchronized (mChanges) { |
153 | if (mApplyCalled) throw new IllegalStateException(); |
154 | mChanges.put(key, value); |
155 | return this; |
156 | } |
157 | } |
158 | |
159 | @Override |
160 | public SharedPreferences.Editor putLong(String key, long value) { |
161 | synchronized (mChanges) { |
162 | if (mApplyCalled) throw new IllegalStateException(); |
163 | mChanges.put(key, value); |
164 | return this; |
165 | } |
166 | } |
167 | |
168 | @Override |
169 | public SharedPreferences.Editor putFloat(String key, float value) { |
170 | synchronized (mChanges) { |
171 | if (mApplyCalled) throw new IllegalStateException(); |
172 | mChanges.put(key, value); |
173 | return this; |
174 | } |
175 | } |
176 | |
177 | @Override |
178 | public SharedPreferences.Editor putBoolean(String key, boolean value) { |
179 | synchronized (mChanges) { |
180 | if (mApplyCalled) throw new IllegalStateException(); |
181 | mChanges.put(key, value); |
182 | return this; |
183 | } |
184 | } |
185 | |
186 | @Override |
187 | public SharedPreferences.Editor remove(String key) { |
188 | synchronized (mChanges) { |
189 | if (mApplyCalled) throw new IllegalStateException(); |
190 | // Magic value for removes |
191 | mChanges.put(key, this); |
192 | return this; |
193 | } |
194 | } |
195 | |
196 | @Override |
197 | public SharedPreferences.Editor clear() { |
198 | synchronized (mChanges) { |
199 | if (mApplyCalled) throw new IllegalStateException(); |
200 | mClearCalled = true; |
201 | return this; |
202 | } |
203 | } |
204 | |
205 | @Override |
206 | public boolean commit() { |
207 | apply(); |
208 | return true; |
209 | } |
210 | |
211 | @Override |
212 | public void apply() { |
213 | synchronized (mData) { |
214 | synchronized (mChanges) { |
215 | if (mApplyCalled) throw new IllegalStateException(); |
216 | if (mClearCalled) { |
217 | mData.clear(); |
218 | } |
219 | for (Map.Entry<String, Object> entry : mChanges.entrySet()) { |
220 | String key = entry.getKey(); |
221 | Object value = entry.getValue(); |
222 | if (value == this) { |
223 | // Special value for removal |
224 | mData.remove(key); |
225 | } else { |
226 | mData.put(key, value); |
227 | } |
228 | } |
229 | // The real shared prefs clears out the temporaries allowing the caller to |
230 | // reuse the Editor instance, however this is undocumented behavior and subtle |
231 | // to read, so instead we just ban any future use of this instance. |
232 | mApplyCalled = true; |
233 | } |
234 | } |
235 | } |
236 | } |
237 | |
238 | } |