/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Nuxeo - initial API and implementation * * $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $ */ package org.eclipse.ecr.core.api.impl; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.ecr.core.api.DocumentModel; import org.eclipse.ecr.core.api.DocumentModelIterator; import org.eclipse.ecr.core.api.DocumentModelList; import org.eclipse.ecr.core.api.PagedDocumentsProvider; import org.eclipse.ecr.core.api.SortInfo; /** * Keeps track of current page and previous pages loaded from document iterator. * * @author <a href="mailto:dm@nuxeo.com">Dragos Mihalache</a> */ public class DocumentsPageProvider implements PagedDocumentsProvider { /** * Generated serial version uid. */ private static final long serialVersionUID = 1016097877999734437L; private static final Log log = LogFactory.getLog( DocumentsPageProvider.class); /** * Reference to Documents iterator from which this class is feeding pages. */ private final DocumentModelIterator docsIterator; private final int pageSize; private int currentPageIndex = -1; // local cache of loaded pages as a contiguous list from page 0 // TODO limit the cache number... private final List<DocumentModelList> loadedPages = new ArrayList<DocumentModelList>(); private long totalResultsCount; private String providerName; /** * Constructor taking as argument an iterator. The iterator is considered * unaltered. */ public DocumentsPageProvider(DocumentModelIterator docsIterator, int pageSize) { this.docsIterator = docsIterator; this.pageSize = pageSize; totalResultsCount = docsIterator.size(); } public void setCurrentPage(int page) { // insure we have loaded the requested page if // currentPageIndex is updated by this // TODO is it correct to have the page loaded when set? getPage(page); } @Override public int getCurrentPageIndex() { return currentPageIndex; } @Override public DocumentModelList getCurrentPage() { if (currentPageIndex == -1) { // avanse return getPage(0); } return getPage(currentPageIndex); } @Override public DocumentModelList getPage(int page) { if (page < 0) { page = 0; } DocumentModelList docsPage = null; if (loadedPages.size() > page) { // we have it already retrieved docsPage = loadedPages.get(page); currentPageIndex = page; } else { // forward to the required page while (currentPageIndex < page) { if (docsIterator.hasNext()) { docsPage = loadNextPage(); // cache the page loadedPages.add(docsPage); } else { // requested page out of limit // empty list // TODO : maybe reset to the first page docsPage = new DocumentModelListImpl(); break; } } } return docsPage; } @Override public boolean isNextPageAvailable() { return docsIterator.hasNext() || currentPageIndex < loadedPages.size() - 1; } /** * Creates the DocumentModelList for the next page. * Doesn't put it in the loadedPages cache. */ private DocumentModelList loadNextPage() { if (!docsIterator.hasNext()) { return new DocumentModelListImpl(); } currentPageIndex++; DocumentModelList docsPage = new DocumentModelListImpl(); for (int i = 0; i < pageSize; i++) { if (docsIterator.hasNext()) { try { final DocumentModel docModel = docsIterator.next(); docsPage.add(docModel); } catch (NoSuchElementException e) { log.error("Unpredicted end of iterator !!"); endReached(i); break; } } else { endReached(i); break; } } return docsPage; } /** * Used to update some member fields from the new knowledge that * end was reached. * * @param posInPage the position in current page when this happened */ private void endReached(int posInPage) { if (DocumentModelIterator.UNKNOWN_SIZE == totalResultsCount) { totalResultsCount = pageSize * currentPageIndex + posInPage; } } @Override public long getResultsCount() { long resultsCount = totalResultsCount; if (DocumentModelIterator.UNKNOWN_SIZE == resultsCount) { resultsCount = UNKNOWN_SIZE; } return resultsCount; } @Override public boolean isPreviousPageAvailable() { return currentPageIndex > 0; } @Override public void last() { int lastPage = getNumberOfPages(); if (lastPage == UNKNOWN_SIZE) { while (isNextPageAvailable()) { getNextPage(); } } else { setCurrentPage(lastPage - 1); } } @Override public DocumentModelList getNextPage() { return getPage(currentPageIndex + 1); } @Override public void next() { getPage(currentPageIndex + 1); } @Override public void previous() { if (currentPageIndex > 0) { setCurrentPage(currentPageIndex - 1); } } @Override public void rewind() { getPage(0); } @Override public int getNumberOfPages() { long size = docsIterator.size(); if (size == DocumentModelIterator.UNKNOWN_SIZE) { return UNKNOWN_SIZE; } return (int) (1 + (size - 1) / pageSize); } /** * Nothing can't be done to refresh this provider's pages * the whole provider should be instead replaced */ @Override public void refresh() { } // TODO stop duplication @Override public String getCurrentPageStatus() { int total = getNumberOfPages(); int current = currentPageIndex + 1; if (total == UNKNOWN_SIZE) { return String.format("%d", current); } else { return String.format("%d/%d", current, total); } } @Override public int getCurrentPageOffset() { // Might become inappropriate if previous documents have changed return currentPageIndex * pageSize; } @Override public int getCurrentPageSize() { // page getters are cached, hence cheap return getCurrentPage().size(); } @Override public int getPageSize() { return pageSize; } @Override public String getName() { return providerName; } @Override public SortInfo getSortInfo() { return null; } @Override public boolean isSortable() { return false; } @Override public void setName(String name) { providerName = name; } }