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 5package org.chromium.base.test.util; 6 7import android.content.SharedPreferences; 8 9import java.util.Collections; 10import java.util.HashMap; 11import java.util.Map; 12import 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 */ 19public 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} 239