package by.istin.android.xcore.source; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import by.istin.android.xcore.provider.ModelContract; import by.istin.android.xcore.utils.Holder; import by.istin.android.xcore.utils.Log; import by.istin.android.xcore.utils.StringUtil; import by.istin.android.xcore.utils.UriUtils; public class DataSourceRequest { public static final long CACHE_EXPIRATION_NONE = -1l; private static final String REQUEST_URI = "___ruri"; private static final String REQUEST_PARENT_URI = "___parent_ruri"; private static final String REQUEST_CACHEABLE = "___c"; private static final String REQUEST_FORCE_UPDATE_DATA = "___fud"; private static final String REQUEST_CACHE_EXPIRATION = "___exp"; private static final String JOIN_REQUEST = "___joinrequest"; private static final String JOIN_PROCESSOR_KEY = "___joinprocessor"; private static final String JOIN_DATASOURCE_KEY = "___joindatasource"; private static final String[] KEYS = new String[] { REQUEST_URI, REQUEST_PARENT_URI, REQUEST_CACHEABLE, REQUEST_FORCE_UPDATE_DATA, REQUEST_CACHE_EXPIRATION }; private Bundle mBundle = new Bundle(); private Map<String, Holder<String>> mParamsCache = new ConcurrentHashMap<String, Holder<String>>(); private Uri mCacheUri = null; public DataSourceRequest() { } public DataSourceRequest(String requestDataUri) { setUri(requestDataUri); } public void setUri(String uri) { mCacheUri = null; mBundle.putString(REQUEST_URI, uri); } public String getUri() { return mBundle.getString(REQUEST_URI); } public Uri getRequestUri() { if (mCacheUri == null) { mCacheUri = Uri.parse(getUri()); } return mCacheUri; } public void setCacheable(boolean isCacheable) { mBundle.putString(REQUEST_CACHEABLE, String.valueOf(isCacheable)); } public boolean isCacheable() { String value = mBundle.getString(REQUEST_CACHEABLE); return Boolean.valueOf(value); } public void setParentUri(String parentUri) { mBundle.putString(REQUEST_PARENT_URI, parentUri); } public String getRequestParentUri() { return mBundle.getString(REQUEST_PARENT_URI); } public void setForceUpdateData(boolean isForceFreshData) { mBundle.putString(REQUEST_FORCE_UPDATE_DATA, String.valueOf(isForceFreshData)); } public boolean isForceUpdateData() { String value = mBundle.getString(REQUEST_FORCE_UPDATE_DATA); return Boolean.valueOf(value); } public void setCacheExpiration(long cacheExpirationInMillis) { mBundle.putString(REQUEST_CACHE_EXPIRATION, String.valueOf(cacheExpirationInMillis)); } public long getCacheExpiration() { String value = mBundle.getString(REQUEST_CACHE_EXPIRATION); if (TextUtils.isEmpty(value)) { return CACHE_EXPIRATION_NONE; } else { return Long.parseLong(value); } } public void putParam(String key, String value) { checkIfParamIsNotRestricted(key); mBundle.putString(key, value); } public void putStringParams(Bundle bundle) { Set<String> params = bundle.keySet(); for (String key : params) { checkIfParamIsNotRestricted(key); } mBundle.putAll(bundle); } private void checkIfParamIsNotRestricted(String key) { if (Log.isOff) return; for (String privateKey : KEYS) { if (privateKey.equalsIgnoreCase(key)) { throw new IllegalArgumentException(key + " is reserved by DataSourceRequest class and can't be used."); } } } public String getParam(String key) { String value = mBundle.getString(key); if (value == null) { Holder<String> stringHolder = mParamsCache.get(key); value = stringHolder == null ? null : stringHolder.get(); if (value == null) { String queryParameter = getRequestUri().getQueryParameter(key); mParamsCache.put(key, new Holder<String>(queryParameter)); return queryParameter; } } return value; } public String toUriParams() { StringBuilder buffer = new StringBuilder(); Set<String> keySet = mBundle.keySet(); List<String> sortedKeys = new ArrayList<String>(); sortedKeys.addAll(keySet); Collections.sort(sortedKeys); for (Iterator<String> iterator = sortedKeys.iterator(); iterator.hasNext();) { String key = iterator.next(); buffer.append(key); buffer.append("="); //double encoding for correct decoding pagings uris buffer.append(StringUtil.encode(StringUtil.encode(mBundle.getString(key)))); if (iterator.hasNext()) { buffer.append("&"); } } return buffer.toString(); } public void toIntent(Intent intent) { ModelContract.dataSourceRequestToIntent(intent, mBundle); } public void toBundle(Bundle bundle) { ModelContract.dataSourceRequestToBundle(bundle, mBundle); } public void joinRequest(DataSourceRequest dataSourceRequest, String processorKey, String dataSourceKey) { if (dataSourceRequest == null) { throw new IllegalArgumentException("dataSourceRequest can not be null"); } if (processorKey == null) { throw new IllegalArgumentException("processorKey can not be null"); } if (dataSourceKey == null) { throw new IllegalArgumentException("dataSourceKey can not be null"); } mBundle.putString(JOIN_REQUEST, dataSourceRequest.toUriParams()); mBundle.putString(JOIN_PROCESSOR_KEY, processorKey); mBundle.putString(JOIN_DATASOURCE_KEY, dataSourceKey); } public DataSourceRequest getJoinedRequest() { String joinedRequest = mBundle.getString(JOIN_REQUEST); if (StringUtil.isEmpty(joinedRequest)) { return null; } return fromUri(Uri.parse("content://temp?"+joinedRequest)); } public String getJoinedProcessorKey() { return mBundle.getString(JOIN_PROCESSOR_KEY); } public String getJoinedDataSourceKey() { return mBundle.getString(JOIN_DATASOURCE_KEY); } public static DataSourceRequest fromBundle(Bundle bundle) { DataSourceRequest data = new DataSourceRequest(); Bundle dataSourceFromBundle = ModelContract.getDataSourceFromBundle(bundle); if (dataSourceFromBundle != null) { data.mBundle = dataSourceFromBundle; } return data; } public static DataSourceRequest fromIntent(Intent intent) { DataSourceRequest data = new DataSourceRequest(); Bundle dataSourceFromIntent = ModelContract.getDataSourceFromIntent(intent); if (dataSourceFromIntent != null) { data.mBundle = dataSourceFromIntent; } return data; } public static DataSourceRequest fromUri(Uri uri) { DataSourceRequest requestData = new DataSourceRequest(); Set<String> queryParameters = UriUtils.getQueryParameters(uri); for (String key : queryParameters) { String value = uri.getQueryParameter(key); if (!TextUtils.isEmpty(value)) { requestData.mBundle.putString(key, StringUtil.decode(value)); } } return requestData; } public static class JoinedRequestBuilder { private class RequestConfig { private String processorKey; private String dataSourceKey; private DataSourceRequest dataSourceRequest; } private final List<RequestConfig> dataSourceRequests = new ArrayList<RequestConfig>(); private String dataSourceKey; private String processorKey; public JoinedRequestBuilder(DataSourceRequest dataSourceRequest) { RequestConfig requestConfig = new RequestConfig(); requestConfig.dataSourceRequest = dataSourceRequest; dataSourceRequests.add(requestConfig); } public JoinedRequestBuilder setDataSource(String dataSourceKey) { this.dataSourceKey = dataSourceKey; return this; } public JoinedRequestBuilder setProcessor(String processorKey) { this.processorKey = processorKey; return this; } public String getDataSourceKey() { return dataSourceKey; } public String getProcessorKey() { return processorKey; } public JoinedRequestBuilder add(DataSourceRequest dataSourceRequest) { if (StringUtil.isEmpty(dataSourceKey)) { throw new IllegalStateException("default dataSource is not set, call JoinedRequestBuilder.setDataSource to set"); } if (StringUtil.isEmpty(processorKey)) { throw new IllegalStateException("default processorKey is not set, call JoinedRequestBuilder.setProcessor to set"); } return add(dataSourceRequest, processorKey, dataSourceKey); } public JoinedRequestBuilder add(DataSourceRequest dataSourceRequest, String processorKey) { if (StringUtil.isEmpty(dataSourceKey)) { throw new IllegalStateException("default dataSource is not set, call JoinedRequestBuilder.setDataSource to set"); } return add(dataSourceRequest, processorKey, dataSourceKey); } public JoinedRequestBuilder add(DataSourceRequest dataSourceRequest, String processorKey, String dataSourceKey) { RequestConfig requestConfig = new RequestConfig(); requestConfig.dataSourceRequest = dataSourceRequest; requestConfig.processorKey = processorKey; requestConfig.dataSourceKey = dataSourceKey; dataSourceRequests.add(requestConfig); return this; } public DataSourceRequest build() { if (dataSourceRequests.isEmpty()) { return null; } for (int i = dataSourceRequests.size()-1; i > 0; i--) { RequestConfig requestConfig = dataSourceRequests.get(i); RequestConfig prevRequest = dataSourceRequests.get(i-1); prevRequest.dataSourceRequest.joinRequest(requestConfig.dataSourceRequest, requestConfig.processorKey, requestConfig.dataSourceKey); } return dataSourceRequests.get(0).dataSourceRequest; } } }