javaandroidgenericscachingrobospice

How generify class with T and List<T>


I am trying to generify my class structure.
I will show my real structure to be more specific.

I am writing application with offline mode support, so I decided to implement my ETag cache mechanism in using Robospice and GreenDao ORM.

I need to cache only GET requests.

Firstly my requests should extend base request(not mine), in my case RetrofitSpiceRequest<T, V>

T is type of return data   
V is service type, in my case I am  using Retrofit.

The problem is that return type is not List of T types by default and I need to create subclass that extends array of T objects and that use it as return type.

Something like this

public class City {
....
....
....
    public static class List extends ArrayList<City> {
    .....
    .....
    }

}

And use City.List as return type.

But I have my DAO declared as following

public class CityDao extends AbstractDao<City, Long> {

}

In each request (GET) I need to have specific DAO as a member in order to cache data if it differs from the server data. Or load data from the local database if there is no connection.

The problem here is that request generified by T type which is mostly list, City.List in my case, of some objects, but my dao is generified by, for example E type which is City in my case.

I want to create method like this

public AbastractDao<T,Long> getRequestDao() {

}

But as far as my Request returns City.List, I have no idea how to generify this class, I feel that it is possible, but now no ideas.
In case of non generic dao method, I have to duplicate code like this

 @Override
    public void insertReceivedData(City.List received) {
        mCityDao.insertOrReplaceInTx(received);
    }

 @Override
    public City.List getCachedData() {
        if (mFilterMap != null && mFilterMap.size() > 0) {
            return (City.List) mCityDao.loadAll();
        } else {
            WhereCondition[] whereConditions = QueryUtils.convertPropertyMapToConditionalArray(mFilterMap);
            return (City.List) mCityDao.queryBuilder().where(whereConditions[0], Arrays.copyOfRange(whereConditions, 1, whereConditions.length)).list();
        }
    }

In each request

Please share your ideas.

Thanks.


Solution

  • I end up with following solution. It is not as good as I wanted, but it works and better than duplicating code.

    My base request class.

    public abstract class BaseGetRequest<L extends List<T>, T, V> extends RetrofitSpiceRequest<L, V> implements FilterableRequest {
        // Context
        protected Context mContext;
        // Filter used in request and in queries
        protected Map<Property, String> mFilterMap;
        // Session provided Singletone
        protected DaoSessionProvider mSessionProvider;
    
        public BaseGetRequest(Class<L> clazz, Class<V> retrofitedInterfaceClass, Context context, Map<Property, String> filterMap) {
            super(clazz, retrofitedInterfaceClass);
            mContext = context;
            mFilterMap = filterMap;
            mSessionProvider = ((DaoSessionProvider) mContext.getApplicationContext());
            // TODO determine required retry count
            setRetryPolicy(new RetryPolicy() {
                @Override
                public int getRetryCount() {
                    return 0;
                }
    
                @Override
                public void retry(SpiceException e) {
    
                }
    
                @Override
                public long getDelayBeforeRetry() {
                    return 0;
                }
            });
        }
    
        protected WhereCondition[] getWhereConditions() {
            return QueryUtils.convertPropertyMapToConditionalArray(mFilterMap);
        }
    
        public BaseGetRequestV2(Class<L> clazz, Class<V> retrofitedInterfaceClass, Context context) {
            this(clazz, retrofitedInterfaceClass, context, null);
        }
    
        public abstract AbstractDao<T, Long> getDao();
    
        public abstract L createDataList(List<T> list);
    
        public L getCachedData() {
            if (mFilterMap != null && mFilterMap.size() > 0) {
                WhereCondition[] whereConditions = getWhereConditions();
                return createDataList(getDao().queryBuilder().where(whereConditions[0], Arrays.copyOfRange(whereConditions, 1, whereConditions.length)).list());
            } else {
                return createDataList(getDao().loadAll());
            }
        }
    
        public abstract L getData();
    
        @Override
        public Map<Property, String> getFilterMap() {
            return mFilterMap;
        }
    
        public Map<String, String> getStringMap() {
            return QueryUtils.convertPropertyMapToString(mFilterMap);
        }
    
        @Override
        public L loadDataFromNetwork() throws Exception {
            L receivedData = null;
            try {
                receivedData = getData();
                WhereCondition[] conditions = getWhereConditions();
                getDao().queryBuilder().where(conditions[0],Arrays.copyOfRange(conditions, 1, conditions.length)).buildDelete().executeDeleteWithoutDetachingEntities();
                getDao().insertOrReplaceInTx(receivedData);
            } catch (Exception ex) {
                receivedData = getCachedData();
            }
            return receivedData;
        }
    }
    

    And I can extend this class like so:

    public class NewsRequest extends BaseGetRequest<NewsArticle.List, NewsArticle, API> {
    
        public static final String TARGET_URL = "/news";
        NewsArticleDao mNewsArticleDao;
    
        public NewsRequest(Context context) {
            this(context, null);
        }
    
        public NewsRequest(Context context, Map<Property, String> filterMap) {
            super(NewsArticle.List.class, API.class, context, filterMap);
            mNewsArticleDao = mSessionProvider.getDaoSession().getNewsArticleDao();
        }
    
        @Override
        public AbstractDao<NewsArticle, Long> getDao() {
            return mNewsArticleDao;
        }
    
        @Override
        public NewsArticle.List createDataList(List<NewsArticle> list) {
           return new NewsArticle.List(list);
        }
    
        @Override
        public NewsArticle.List getData() {
            return getService().getNews(getStringMap());
        }
    }