package in.srain.cube.request;
import android.text.TextUtils;
import in.srain.cube.cache.CacheManager;
import in.srain.cube.cache.CacheResultType;
import in.srain.cube.concurrent.SimpleTask;
import in.srain.cube.util.CLog;
import in.srain.cube.util.CubeDebug;
import java.net.URI;
import java.net.URISyntaxException;
public class CacheAbleRequest<T> extends RequestBase<T> implements ICacheAbleRequest<T> {
public static enum ResultType {
USE_CACHE_NOT_EXPIRED,
USE_CACHE_ANYWAY,
USE_CACHE_ON_TIMEOUT,
USE_DATA_FROM_SERVER,
USE_CACHE_ON_FAIL,
}
protected static final boolean DEBUG = CubeDebug.DEBUG_CACHE;
protected static final String LOG_TAG = "cube-cache-request";
private CacheAbleRequestHandler<T> mHandler;
private T mCacheData;
private boolean mOutOfDate;
private String mCacheKey = null;
private int mTimeout = 0;
private boolean mHasTimeout = false;
private boolean mUseCacheAnyway = false;
private boolean mHasNotified = false;
protected boolean mForceQueryFromServer = false;
private String mInitDataPath;
private boolean mDisableCache = false;
private long mCacheTime;
public CacheAbleRequest() {
}
public CacheAbleRequest(final CacheAbleRequestHandler<T> handler) {
setCacheAbleRequestHandler(handler);
}
public void setCacheAbleRequestHandler(CacheAbleRequestHandler<T> handler) {
mHandler = handler;
}
public void forceQueryFromServer(boolean force) {
mForceQueryFromServer = force;
}
// ===========================================================
// Override parent
// ===========================================================
@Override
public void doSendRequest() {
RequestCacheManager.getInstance().requestCache(this);
}
/**
* Timeout will not be considerate
*
* @return
*/
@Override
protected T doRequestSync() {
T data = RequestCacheManager.getInstance().requestCacheSync(this);
if (data == null) {
data = RequestManager.getInstance().getRequestProxy(this).requestSync(this);
}
return data;
}
/**
* prepare request
*/
@Override
protected void prepareRequest() {
RequestManager.getInstance().getRequestProxy(this).prepareRequest(this);
}
// ===========================================================
// Override Interface
// ===========================================================
@Override
public void setTimeout(int timeOut) {
mTimeout = timeOut;
}
@Override
public CacheAbleRequest<T> setUseCacheAnyway(boolean use) {
mUseCacheAnyway = use;
return this;
}
@Override
public boolean useCacheAnyway() {
return mUseCacheAnyway;
}
@Override
public void onRequestSuccess(T data) {
if (DEBUG) {
CLog.d(LOG_TAG, "%s, onRequestSuccess", getCacheKey());
}
if (hasBeenCanceled()) {
return;
}
if (null != mHandler) {
mHandler.onRequestFinish(data);
// cache data is not available or
// cache is available and time duration not reach timeout or not always use the cache
if (mCacheData == null || (!mHasTimeout && !mUseCacheAnyway)) {
notifyRequestFinish(ResultType.USE_DATA_FROM_SERVER, data, false);
} else {
if (DEBUG) {
CLog.d(LOG_TAG, "%s, will not notifyRequestFinish", getCacheKey());
}
}
}
}
@Override
public void onRequestFail(FailData failData) {
RequestManager.getInstance().getRequestProxy(this).onRequestFail(this, failData);
if (DEBUG) {
CLog.d(LOG_TAG, "%s, onRequestFail", getCacheKey());
}
if (hasBeenCanceled()) {
return;
}
if (null != mHandler) {
mHandler.onRequestFail(failData);
if (mCacheData != null && !cacheIsDisabled() && !mUseCacheAnyway) {
notifyRequestFinish(ResultType.USE_CACHE_ON_FAIL, mCacheData, true);
}
}
}
@Override
public void onNoCacheData(CacheManager cacheManager) {
if (DEBUG) {
CLog.d(LOG_TAG, "%s, onNoCacheData", getCacheKey());
}
if (hasBeenCanceled()) {
return;
}
doQueryFromServer();
beginTimeout();
}
protected void doQueryFromServer() {
RequestManager.getInstance().getRequestProxy(this).sendRequest(this);
}
protected boolean cacheRequestResult() {
return mForceQueryFromServer || !cacheIsDisabled();
}
@Override
public boolean cacheIsDisabled() {
if (mForceQueryFromServer) {
return true;
}
return mDisableCache;
}
// ===========================================================
// Implements Interface {@link ICacheAble}
// ===========================================================
@Override
public void onCacheData(CacheResultType cacheResultType, T data, boolean outOfDate) {
if (DEBUG) {
CLog.d(LOG_TAG, "%s, onQueryFinish, out of date: %s", getCacheKey(), outOfDate);
}
if (hasBeenCanceled()) {
return;
}
mCacheData = data;
mOutOfDate = outOfDate;
if (mHandler != null) {
mHandler.onCacheData(data, outOfDate);
if (mUseCacheAnyway) {
notifyRequestFinish(ResultType.USE_CACHE_ANYWAY, data, mOutOfDate);
} else {
if (!outOfDate) {
notifyRequestFinish(ResultType.USE_CACHE_NOT_EXPIRED, data, false);
}
}
}
}
@Override
public long getCacheTime() {
return mCacheTime;
}
@Override
public String getCacheKey() {
if (mCacheKey == null) {
String cacheKey = null;
String url = getRequestData().getRequestUrl();
try {
URI uri = null;
uri = new URI(url);
cacheKey = uri.getPath();
if (cacheKey.startsWith("/")) {
cacheKey = cacheKey.substring(1);
}
cacheKey = cacheKey.replace("/", "-");
} catch (URISyntaxException e) {
e.printStackTrace();
}
if (TextUtils.isEmpty(cacheKey)) {
throw new RuntimeException("Cache key is null");
}
mCacheKey = cacheKey;
}
return mCacheKey;
}
@Override
public String getAssertInitDataPath() {
return mInitDataPath;
}
@Override
public T onDataFromServer(String data) {
if (DEBUG) {
CLog.d(LOG_TAG, "%s, onDataFromServer", getCacheKey());
}
T ret = super.onDataFromServer(data);
// cache the data
if (!TextUtils.isEmpty(data) && ret != null && cacheRequestResult()) {
RequestCacheManager.getInstance().setCacheData(this.getCacheKey(), data);
}
return ret;
}
@Override
public T processOriginDataFromServer(JsonData rawData) {
rawData = RequestManager.getInstance().getRequestProxy(this).processOriginDataFromServer(this, rawData);
return mHandler.processOriginData(rawData);
}
@Override
public T processRawDataFromCache(JsonData rawData) {
return mHandler.processOriginData(rawData);
}
/**
* will only notify once
*
* @param type
* @param outOfDate
*/
private void notifyRequestFinish(ResultType type, T cacheData, boolean outOfDate) {
if (DEBUG) {
CLog.d(LOG_TAG, "%s, notifyRequestFinish: %s, %s", getCacheKey(), type, outOfDate);
}
if (mHasNotified) {
return;
}
mHasNotified = true;
mHandler.onCacheAbleRequestFinish(cacheData, type, outOfDate);
}
private void timeout() {
mHasTimeout = true;
if (mCacheData != null && mHandler != null) {
notifyRequestFinish(ResultType.USE_CACHE_ON_TIMEOUT, mCacheData, true);
}
}
private void beginTimeout() {
if (mTimeout > 0 && mCacheData != null) {
SimpleTask.postDelay(new Runnable() {
@Override
public void run() {
timeout();
}
}, mTimeout);
}
}
@Override
public CacheAbleRequest<T> setCacheKey(String cacheKey) {
mCacheKey = cacheKey;
return this;
}
@Override
public CacheAbleRequest<T> setDisableCache(boolean disable) {
mDisableCache = disable;
return this;
}
@Override
public CacheAbleRequest<T> setAssertInitDataPath(String path) {
mInitDataPath = path;
return this;
}
@Override
public CacheAbleRequest<T> setCacheTime(long time) {
mCacheTime = time;
return this;
}
}