/* * Copyright (c) 2014-2015 Sean Liu. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.baoti.pioneer.biz.interactor; import android.support.annotation.WorkerThread; import com.github.baoti.pioneer.biz.ResourcePage; import com.github.baoti.pioneer.biz.exception.BizException; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Generic resource pager for elements that can be paged * * @param <E> */ public abstract class ResourcePager<E> { public static final int FIRST_PAGE = 1; public static final ResourcePager EMPTY = new ResourcePager() { @Override protected ResourcePage first() throws BizException { return ResourcePage.EMPTY; } }; @SuppressWarnings("unchecked") public static <E> ResourcePager<E> empty() { return EMPTY; } public static <E> ResourcePager<E> from(final PageInteractor<E> interactor) { return new ResourcePager<E>() { @Override public ResourcePage<E> first() throws BizException { return interactor.interact(); } }; } /** * Next page to request */ protected int page = FIRST_PAGE; /** * Number of pages to request */ protected int count = 1; /** * All resources retrieved */ protected final List<E> resources = new ArrayList<E>(); /** * Are more pages available? */ protected boolean hasMore; protected ResourcePage<E> current; private Changed lastChanged; /** * Reset the number of the next page to be requested from {@link #next()} * and clear all stored state * * @return this pager */ public synchronized ResourcePager<E> reset() { page = FIRST_PAGE; return clear(); } /** * Clear all stored resources and have the next call to {@link #next()} load * all previously loaded pages * * @return this pager */ public synchronized ResourcePager<E> clear() { count = Math.max(1, page - 1); page = FIRST_PAGE; current = null; resources.clear(); hasMore = true; return this; } public boolean hasLoadedResources() { return !resources.isEmpty(); } /** * Get number of resources loaded into this pager * * @return number of resources */ public int size() { return resources.size(); } /** * Get resources * * @return resources */ public List<E> getResources() { return resources; } /** * Get the next page of resources * * @return true if more pages * @throws BizException */ @WorkerThread public synchronized boolean next() throws BizException { boolean emptyPage = false; ResourcePage<E> it = current; List<E> added = new ArrayList<>(); boolean isNew = false; for (int i = 0; i < count; i++) { if (it == null) { isNew = true; it = first(); } else if (it.hasNext()) { it = it.next(); } else { break; } Collection<E> resourcePage = it.getResources(); emptyPage = resourcePage.isEmpty(); if (emptyPage) { break; } for (E resource : resourcePage) { resource = register(resource); if (resource == null) continue; added.add(resource); } } saveLastChanged(isNew, resources.size(), added.size()); resources.addAll(added); current = it; // Set page to count value if first call after call to reset() if (count > 1) { page = count; count = 1; } page++; hasMore = current.hasNext() && !emptyPage; return hasMore; } /** * Are more pages available to request? * * @return true if the last call to {@link #next()} returned true, false * otherwise */ public boolean hasMore() { return hasMore; } /** * Callback to register a fetched resource before it is stored in this pager * <p/> * Sub-classes may override * * @param resource * @return resource */ protected E register(final E resource) { return resource; } protected abstract ResourcePage<E> first() throws BizException; /** * Next page to request */ public int getNextPage() { return page; } public Changed getLastChanged() { return lastChanged; } private void saveLastChanged(final boolean isNew, final int start, final int count) { lastChanged = new Changed() { @Override public boolean isNew() { return isNew; } @Override public int start() { return start; } @Override public int count() { return count; } }; } public interface Changed { boolean isNew(); int start(); int count(); } }