package com.github.droidfu.cachefu;
import java.io.IOException;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Superclass of all objects to be stored in {@link ModelCache}.
*
* To save an object to the cache use {@link #save(ModelCache)}, when updating an object with any
* new data use {@link #reload(ModelCache)}, and when wanting to find an object from the cache, use
* {@link #find(ModelCache, String, Class)}.
*
* @author michaelengland
*
*/
public abstract class CachedModel implements Parcelable {
private String id;
private long transactionId = Long.MIN_VALUE;
/**
* Simple parameter-less constructor. <b>Must</b> also have parameter-less constructor in
* subclasses in order for parceling to work.
*/
public CachedModel() {
}
/**
* Constructor setting variables from parcel. Same as using a blank constructor and calling
* readFromParcel.
*
* @param source
* Parcel to be read from.
* @throws IOException
*/
public CachedModel(Parcel source) throws IOException {
readFromParcel(source);
}
/**
* Constructor setting ID given.
*
* @param id
* ID of new object (used when generating cache key).
*/
public CachedModel(String id) {
this.id = id;
}
/**
* Returns ID of object used in key generation.
*
* @return ID of new object (used when generating cache key).
*/
public String getId() {
return id;
}
/**
* Set ID of object used in key generation.
*
* @param id
* ID of new object (used when generating cache key).
*/
public void setId(String id) {
this.id = id;
}
void setTransactionId(long transactionId) {
this.transactionId = transactionId;
}
/**
* @return Key used to store object in cache. null if id is null.
*/
public String getKey() {
if (id == null) {
return null;
} else {
return createKey(id);
}
}
/**
* Method for looking up object in cache.
*
* @param modelCache
* Cache to be searched.
* @param id
* ID of object to be searched for.
* @param clazz
* Class of desired cached object
* @return Object from cache based upon given parameters.
*/
public static CachedModel find(ModelCache modelCache, String id,
Class<? extends CachedModel> clazz) {
// Create empty object of type
CachedModel testObject;
try {
testObject = clazz.newInstance();
} catch (Exception e) {
return null;
}
// Set ID so key can be generated
testObject.setId(id);
// Attempt to reload object from cache
if (testObject.reload(modelCache)) {
return testObject;
} else {
return null;
}
}
/**
* Attempts to store the object in the cache using this object's key. Generally this is the
* required used.
*
* Overwritten when saving subclasses over the top of separate superclass cache stores. e.g.:
*
* <pre>
* {@code
* public boolean save(ModelCache modelCache) {
* return super.save(modelCache) && super.save(modelCache, super.getKey());
* }
* }
* </pre>
*
* @param modelCache
* Cache to save to.
* @return Whether or not saving to the cache was successful.
*/
public boolean save(ModelCache modelCache) {
return save(modelCache, getKey());
}
/**
* Attempts to save the object in the cache using a given key. Generally only used for saving
* subclasses over the top of separate superclass cache stores.
*
* @param modelCache
* Cache to save to.
* @param saveKey
* Key to be saved under.
* @return Whether or not saving to the cache was successful.
*/
protected boolean save(ModelCache modelCache, String saveKey) {
if ((modelCache != null) && (saveKey != null)) {
modelCache.put(saveKey, this);
return true;
} else {
return false;
}
}
/**
* Attempts to reload any new data from cache.
*
* @param modelCache
* Cache to be reloaded from.
* @return Whether or not newer data was found in the cache.
*/
public boolean reload(ModelCache modelCache) {
String key = getKey();
if ((modelCache != null) && (key != null)) {
CachedModel cachedModel = modelCache.get(key);
if ((cachedModel != null) && (cachedModel.transactionId > this.transactionId)) {
reloadFromCachedModel(modelCache, cachedModel);
this.transactionId = cachedModel.transactionId;
return true;
} else {
return false;
}
} else {
return false;
}
}
/**
* Method called to determine a key in the cache using the object's id e.g.:
*
* <pre>
* {@code
* public String createKey(String id) {
* "example_object_" + id;
* }
* }
* </pre>
*
* @param id
* ID of object to be stored.
* @return Key that object with given ID should be stored in cache under.
*/
public abstract String createKey(String id);
/**
* Method called to reload any data from a more recently stored object e.g.:
*
* <pre>
* {@code
* public boolean reloadFromCachedModel(ModelCache modelCache, CachedModel cachedModel) {
* ExampleObject cachedExampleObject = (ExampleObject) cachedModel;
* this.exampleVariable = cachedExampleObject.exampleVariable;
* return false;
* }
* }
* </pre>
*
* Can also be used to reload internal cached objects. e.g.:
*
* <pre>
* {@code
* public boolean reloadFromCachedModel(ModelCache modelCache, CachedModel cachedModel) {
* ExampleObject cachedExampleObject = (ExampleObject) cachedModel;
* this.exampleInternalCachedObject = cachedExampleObject.exampleInternalCachedObject;
* return this.exampleInternalCachedObject.reload(modelCache);
* }
* }
* </pre>
*
* @param modelCache
* Cache that is currently being reloaded from.
* @param cachedModel
* Latest version of object in cache.
* @return Whether or not an internal cached object was updated (useful for optimization
* purposes, especially on lists).
*/
public abstract boolean reloadFromCachedModel(ModelCache modelCache, CachedModel cachedModel);
/**
* @see android.os.Parcelable#describeContents()
*/
@Override
public int describeContents() {
return 0;
}
/**
* @see android.os.Parcelable#writeToParcel(android.os.Parcel, int)
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeLong(transactionId);
}
/**
* Saves data to parcel.
*
* @param source
* Parcel to save to.
* @throws IOException
*/
public void readFromParcel(Parcel source) throws IOException {
id = source.readString();
transactionId = source.readLong();
}
}