package com.openfeint.internal;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.StreamCorruptedException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import com.openfeint.internal.OpenFeintInternal;
public class SyncedStore {
public class Editor {
public void putString(String k, String v) {
SyncedStore.this.mMap.put(k, v);
}
public void remove(String k) {
SyncedStore.this.mMap.remove(k);
}
public Set<String> keySet() {
// duplicate the key set, so if we remove/add keys, we don't get
// a ConcurrentModificationException
return new HashSet<String>( mMap.keySet() );
}
public void commit() {
save();
mLock.writeLock().unlock();
}
};
Editor edit() {
mLock.writeLock().lock();
return new Editor();
}
public class Reader {
public String getString(String k, String defValue) {
String rv = mMap.get(k);
return rv != null ? rv : defValue;
}
public Set<String> keySet() {
return mMap.keySet();
}
public void complete() {
mLock.readLock().unlock();
}
}
Reader read() {
mLock.readLock().lock();
return new Reader();
}
private static final String FILENAME = "of_prefs";
private static final String TAG = "DistributedPrefs";
private HashMap<String, String> mMap;
private Context mContext;
private ReentrantReadWriteLock mLock;
public SyncedStore(Context c) {
mContext = c;
mMap = new HashMap<String, String>();
mLock = new ReentrantReadWriteLock();
load();
}
public void load() {
mMap = null;
boolean mustSaveAfterLoad = false;
long start = System.currentTimeMillis();
File myStore = mContext.getFileStreamPath(FILENAME);
mLock.writeLock().lock();
try {
final PackageManager packageManager = mContext.getPackageManager();
List<ApplicationInfo> apps = packageManager.getInstalledApplications(0);
ApplicationInfo myInfo = null;
for (ApplicationInfo ai : apps) {
if (ai.packageName.equals(mContext.getPackageName())) {
myInfo = ai;
break;
}
}
final String myStoreCPath = myStore.getCanonicalPath();
if (myInfo != null && myStoreCPath.startsWith(myInfo.dataDir)) {
String underDataDir = myStoreCPath.substring(myInfo.dataDir.length());
for( ApplicationInfo ai : apps ) {
File otherStore = new File(ai.dataDir, underDataDir);
if (myStore.lastModified() < otherStore.lastModified()) {
// strict inequality means that this store is NOT from myInfo, therefore
// we have to save it in our own location once we've loaded it.
mustSaveAfterLoad = true;
myStore = otherStore;
}
}
mMap = mapFromStore(myStore);
}
if (mMap == null) mMap = new HashMap<String, String>();
} catch (IOException e1) {
OpenFeintInternal.log(TAG, "broken");
} finally {
mLock.writeLock().unlock();
}
if (mustSaveAfterLoad) {
save();
}
long elapsed = System.currentTimeMillis() - start;
OpenFeintInternal.log(TAG, "Loading prefs took " + new Long(elapsed).toString() + " millis");
}
@SuppressWarnings("unchecked")
private HashMap<String, String> mapFromStore(File myStore) {
InputStream is = null;
ObjectInputStream ois = null;
try {
is = new FileInputStream(myStore);
ois = new ObjectInputStream(is);
Object o = ois.readObject();
if (o != null && o instanceof HashMap<?,?>) {
return (HashMap<String, String>)o;
}
} catch (FileNotFoundException e) {
OpenFeintInternal.log(TAG, "Couldn't open " + FILENAME);
} catch (StreamCorruptedException e) {
OpenFeintInternal.log(TAG, "StreamCorruptedException");
} catch (IOException e) {
OpenFeintInternal.log(TAG, "IOException while reading");
} catch (ClassNotFoundException e) {
OpenFeintInternal.log(TAG, "ClassNotFoundException");
} finally {
try {
if (ois != null) {
ois.close();
} else if (is != null) {
is.close();
}
} catch (IOException e) {
OpenFeintInternal.log(TAG, "IOException while cleaning up");
}
}
return null;
}
public void save() {
OutputStream os = null;
ObjectOutputStream oos = null;
mLock.readLock().lock();
try {
os = mContext.openFileOutput(FILENAME, Context.MODE_WORLD_READABLE);
oos = new ObjectOutputStream(os);
oos.writeObject(mMap);
} catch (IOException e) {
OpenFeintInternal.log(TAG, "Couldn't open " + FILENAME + " for writing");
} finally {
try {
if (oos != null) {
oos.close();
} else if (os != null) {
os.close();
}
} catch (IOException e) {
OpenFeintInternal.log(TAG, "IOException while cleaning up");
} finally {
mLock.readLock().unlock();
}
}
}
}