org.apache.wicket.markup.repeater.AbstractPageableView.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wicket.markup.repeater.AbstractPageableView.java

Source

/*
 * 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.apache.wicket.markup.repeater;

import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;

import org.apache.wicket.markup.html.navigation.paging.IPageableItems;
import org.apache.wicket.model.IModel;

/**
 * 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>
 * 
 * @see org.apache.wicket.markup.repeater.RefreshingView
 * @see org.apache.wicket.markup.html.navigation.paging.IPageable
 * 
 * @author Igor Vaynberg (ivaynberg)
 * 
 * @param <T>
 *            type of elements contained in the model's list
 */
public 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);
    }

    /** @see org.apache.wicket.Component#Component(String) */
    public AbstractPageableView(String id) {
        super(id);
    }

    /**
     * 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 = null;
    }

    // /////////////////////////////////////////////////////////////////////////
    // 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 final 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.
     * 
     * @see #getItemCount()
     * 
     * @return total item count, but 0 if not visible in the hierarchy
     */
    public final long getRowCount() {
        if (!isVisibleInHierarchy()) {
            return 0;
        }

        return getItemCount();
    }

    /**
     * Get the item count. Since dataprovider.size() could potentially be expensive, the item count
     * is cached.
     * 
     * @see #getRowCount()
     * 
     * @return the item count
     */
    @Override
    public final long getItemCount() {
        if (cachedItemCount != null) {
            return cachedItemCount;
        }

        long count = internalGetItemCount();

        cachedItemCount = 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 final void setCurrentPage(long page) {
        if (currentPage != page) {
            if (isVersioned()) {
                addStateChange();

            }
        }
        currentPage = page;
    }

    /**
     * @see org.apache.wicket.markup.html.navigation.paging.IPageable#getPageCount()
     */
    @Override
    public 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
    // /////////////////////////////////////////////////////////////////////////

    /**
     * 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 long index;
        private final Iterator<IModel<T>> delegate;

        /**
         * 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 java.util.Iterator#remove()
         */
        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }

        /**
         * @see java.util.Iterator#hasNext()
         */
        @Override
        public boolean hasNext() {
            return (index < max) && delegate.hasNext();
        }

        /**
         * @see java.util.Iterator#next()
         */
        @Override
        public IModel<T> next() {
            if (index >= max) {
                throw new NoSuchElementException();
            }
            index++;
            return delegate.next();
        }

    }

    /**
     * @see org.apache.wicket.Component#onDetach()
     */
    @Override
    protected void onDetach() {
        clearCachedItemCount();
        super.onDetach();
    }
}