/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.cyclop.web.components.iterablegrid; import org.apache.wicket.markup.html.navigation.paging.IPageableItems; import org.apache.wicket.markup.repeater.RefreshingView; import org.apache.wicket.model.IModel; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; // #################################################################################################################### // This class has been copied from wicket 6.14.0 // Only minimal modifications has been made in order to simplify future updates to new wicket version, those are: // - removed final from method setCurrentPage(long) - method can be overwritten now // - removed final from method setItemsPerPage(long) - method can be overwritten now // - AbstractPageableView is package protected // #################################################################################################################### /** * An abstract repeater view that provides paging functionality to its * subclasses. * <p> * The view is populated by overriding the * <code>getItemModels(int offset, int count)</code> method and providing an * iterator that returns models for items in the current page. The * AbstractPageableView builds the items that will be rendered by looping over * the models and calling the * <code>newItem(String id, int index, IModel model)</code> to generate the * child item container followed by <code>populateItem(Component item)</code> to * let the user populate the newly created item container with with custom * components. * </p> * * @param <T> * Model object type * @author Igor Vaynberg (ivaynberg) * @see org.apache.wicket.markup.repeater.RefreshingView * @see org.apache.wicket.markup.html.navigation.paging.IPageable */ abstract class AbstractPageableView<T> extends RefreshingView<T> implements IPageableItems { /** */ private static final long serialVersionUID = 1L; /** * Keeps track of the number of items we show per page. The default is * Integer.MAX_VALUE which effectively disables paging. */ private long itemsPerPage = Long.MAX_VALUE; /** Keeps track of the current page number. */ private long currentPage; /** * <code>cachedItemCount</code> is used to cache the call to * <code>internalGetItemCount()</code> for the duration of the request * because that call can potentially be expensive ( a select count query ) * and so we do not want to execute it multiple times. */ private transient long cachedItemCount; /** * Constructor * * @param id * @param model * @see org.apache.wicket.Component#Component(String, IModel) */ public AbstractPageableView(String id, IModel<? extends Collection<? extends T>> model) { super(id, model); clearCachedItemCount(); } /** @see org.apache.wicket.Component#Component(String) */ public AbstractPageableView(String id) { super(id); clearCachedItemCount(); } /** * This method retrieves the subset of models for items in the current page * and allows RefreshingView to generate items. * * @return iterator over models for items in the current page */ @Override protected Iterator<IModel<T>> getItemModels() { long offset = getFirstItemOffset(); long size = getViewSize(); Iterator<IModel<T>> models = getItemModels(offset, size); models = new CappedIteratorAdapter<T>(models, size); return models; } /** @see org.apache.wicket.markup.repeater.AbstractRepeater#onBeforeRender() */ @Override protected void onBeforeRender() { clearCachedItemCount(); super.onBeforeRender(); } /** * Returns an iterator over models for items in the current page * * @param offset * index of first item in this page * @param size * number of items that will be shown in the current page * @return an iterator over models for items in the current page */ protected abstract Iterator<IModel<T>> getItemModels(long offset, long size); // ///////////////////////////////////////////////////////////////////////// // ITEM COUNT CACHE // ///////////////////////////////////////////////////////////////////////// private void clearCachedItemCount() { cachedItemCount = -1; } private long getCachedItemCount() { if (cachedItemCount < 0) { throw new IllegalStateException("getItemCountCache() called when cache was not set"); } return cachedItemCount; } private void setCachedItemCount(long itemCount) { cachedItemCount = itemCount; } private boolean isItemCountCached() { return cachedItemCount >= 0; } // ///////////////////////////////////////////////////////////////////////// // PAGING // ///////////////////////////////////////////////////////////////////////// /** @return maximum number of items that will be shown per page */ @Override public long getItemsPerPage() { return itemsPerPage; } /** * Sets the maximum number of items to show per page. The current page will * also be set to zero * * @param items */ public void setItemsPerPage(long items) { if (items < 1) { throw new IllegalArgumentException("Argument [itemsPerPage] cannot be less than 1"); } if (itemsPerPage != items) { if (isVersioned()) { addStateChange(); } } itemsPerPage = items; // because items per page can effect the total number of pages we always // reset the current page back to zero setCurrentPage(0); } /** @return total item count */ protected abstract long internalGetItemCount(); /** * Get the row count. * * @return total item count, but 0 if not visible in the hierarchy * @see #getItemCount() */ public final long getRowCount() { if (!isVisibleInHierarchy()) { return 0; } return getItemCount(); } /** * Get the item count. Since dataprovider.readSize() could potentially be * expensive, the item count is cached. * * @return the item count * @see #getRowCount() */ @Override public final long getItemCount() { if (isItemCountCached()) { return getCachedItemCount(); } long count = internalGetItemCount(); setCachedItemCount(count); return count; } /** @see org.apache.wicket.markup.html.navigation.paging.IPageable#getCurrentPage() */ @Override public final long getCurrentPage() { long page = currentPage; /* * trim current page if its out of bounds this can happen if items are * added/deleted between requests */ if (page > 0 && page >= getPageCount()) { page = Math.max(getPageCount() - 1, 0); currentPage = page; return page; } return page; } /** @see org.apache.wicket.markup.html.navigation.paging.IPageable#setCurrentPage(long) */ @Override public void setCurrentPage(long page) { if (currentPage != page) { if (isVersioned()) { addStateChange(); } } currentPage = page; } /** @see org.apache.wicket.markup.html.navigation.paging.IPageable#getPageCount() */ @Override public final long getPageCount() { long total = getRowCount(); long itemsPerPage = getItemsPerPage(); long count = total / itemsPerPage; if (itemsPerPage * count < total) { count++; } return count; } /** @return the index of the first visible item in the view */ public long getFirstItemOffset() { return getCurrentPage() * getItemsPerPage(); } /** @return the number of items visible */ public long getViewSize() { return Math.min(getItemsPerPage(), getRowCount() - getFirstItemOffset()); } // ///////////////////////////////////////////////////////////////////////// // HELPER CLASSES // ///////////////////////////////////////////////////////////////////////// /** @see org.apache.wicket.Component#onDetach() */ @Override protected void onDetach() { clearCachedItemCount(); super.onDetach(); } private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in all fields s.defaultReadObject(); clearCachedItemCount(); } /** * Iterator adapter that makes sure only the specified max number of items * can be accessed from its delegate. * * @param <T> * Model object type */ private static class CappedIteratorAdapter<T> implements Iterator<IModel<T>> { private final long max; private final Iterator<IModel<T>> delegate; private long index; /** * Constructor * * @param delegate * delegate iterator * @param max * maximum number of items that can be accessed. */ public CappedIteratorAdapter(Iterator<IModel<T>> delegate, long max) { this.delegate = delegate; this.max = max; } /** @see Iterator#remove() */ @Override public void remove() { throw new UnsupportedOperationException(); } /** @see Iterator#hasNext() */ @Override public boolean hasNext() { return (index < max) && delegate.hasNext(); } /** @see Iterator#next() */ @Override public IModel<T> next() { if (index >= max) { throw new NoSuchElementException(); } index++; return delegate.next(); } } }