ru.mystamps.web.util.Pager.java Source code

Java tutorial

Introduction

Here is the source code for ru.mystamps.web.util.Pager.java

Source

/*
 * Copyright (C) 2009-2017 Slava Semushin <slava.semushin@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package ru.mystamps.web.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang3.Validate;

import lombok.Getter;
import lombok.ToString;

// yes, I know that it's too complex :-(
@SuppressWarnings("PMD.ModifiedCyclomaticComplexity")
@ToString
public class Pager {
    // be very careful when you're changing this value
    private static final int MAX_ITEMS = 5;

    private static final int MAX_ITEMS_BEFORE_CURRENT = MAX_ITEMS - 1;
    private static final int MAX_ITEMS_AFTER_CURRENT = MAX_ITEMS - 1;

    private static final int ITEMS_BEFORE_CURRENT = MAX_ITEMS / 2;
    private static final int ITEMS_AFTER_CURRENT = MAX_ITEMS / 2;

    private static final int FIRST_PAGE = 1;

    // this field is shown in toString() and useful when debugging unit tests
    @SuppressWarnings("PMD.SingularField")
    private final int totalRecords;

    // this field is shown in toString() and useful when debugging unit tests
    @SuppressWarnings("PMD.SingularField")
    private final int totalPages;

    // this field is shown in toString() and useful when debugging unit tests
    @SuppressWarnings({ "PMD.SingularField", "PMD.UnusedPrivateField" })
    private final int recordsPerPage;

    // this field is using in the view (hence its getter)
    @Getter
    private final int currentPage;

    // this field is using in the view (hence its getter)
    @Getter
    private final List<Integer> items;

    // this field is using in the view (hence its getter)
    @Getter
    private final Integer prev;

    // this field is using in the view (hence its getter)
    @Getter
    private final Integer next;

    public Pager(long totalRecords, int recordsPerPage, int currentPage) {
        Validate.isTrue(totalRecords >= 0, "Total records must be greater than or equal to zero");
        Validate.isTrue(recordsPerPage > 0, "Records per page must be greater than zero");
        Validate.isTrue(currentPage > 0, "Current page must be greater than zero");

        this.totalRecords = Math.toIntExact(totalRecords);
        this.totalPages = countTotalPages(this.totalRecords, recordsPerPage);
        this.recordsPerPage = recordsPerPage;
        this.currentPage = currentPage;
        this.items = createItems(this.totalRecords, recordsPerPage, currentPage, this.totalPages);
        this.prev = findPrev(currentPage);
        this.next = findNext(currentPage, this.totalPages);
    }

    private static int countTotalPages(int totalRecords, int recordsPerPage) {
        int totalPages = totalRecords / recordsPerPage;
        if (totalRecords % recordsPerPage > 0) {
            totalPages++;
        }
        return totalPages;
    }

    private static Integer findPrev(int currentPage) {
        if (currentPage == FIRST_PAGE) {
            return null;
        }

        return Integer.valueOf(currentPage - 1);
    }

    private static Integer findNext(int currentPage, int totalPages) {
        if (currentPage == totalPages) {
            return null;
        }

        return Integer.valueOf(currentPage + 1);
    }

    // I hope that we'll fix these one day
    @SuppressWarnings("PMD.NPathComplexity")
    private static List<Integer> createItems(int totalRecords, int recordsPerPage, int currentPage,
            int totalPages) {

        if (totalRecords <= recordsPerPage) {
            return Collections.singletonList(FIRST_PAGE);
        }

        int prevItemsCnt = 0;
        for (int i = 1; i <= MAX_ITEMS_BEFORE_CURRENT; i++) {
            int page = currentPage - i;
            if (page < FIRST_PAGE) {
                break;
            }
            prevItemsCnt++;
        }

        int nextItemsCnt = 0;
        for (int i = 1; i <= MAX_ITEMS_AFTER_CURRENT; i++) {
            int page = currentPage + i;
            if (page > totalPages) {
                break;
            }
            nextItemsCnt++;
        }

        // we've added too much to a both sides
        if (prevItemsCnt > ITEMS_BEFORE_CURRENT && nextItemsCnt > ITEMS_AFTER_CURRENT) {
            while (prevItemsCnt > ITEMS_BEFORE_CURRENT) {
                prevItemsCnt--;
            }
            while (nextItemsCnt > ITEMS_AFTER_CURRENT) {
                nextItemsCnt--;
            }

            // CheckStyle: ignore LineLength for next 3 lines
            // we've added too much to the beginning
        } else if (prevItemsCnt > ITEMS_BEFORE_CURRENT && nextItemsCnt <= ITEMS_AFTER_CURRENT) {
            while (prevItemsCnt > ITEMS_BEFORE_CURRENT && (prevItemsCnt + nextItemsCnt + 1) > MAX_ITEMS) {
                prevItemsCnt--;
            }

            // CheckStyle: ignore LineLength for next 3 lines
            // we've added too much to the end
        } else if (nextItemsCnt > ITEMS_AFTER_CURRENT && prevItemsCnt <= ITEMS_BEFORE_CURRENT) {
            while (nextItemsCnt > ITEMS_AFTER_CURRENT && (prevItemsCnt + nextItemsCnt + 1) > MAX_ITEMS) {
                nextItemsCnt--;
            }
        }

        List<Integer> items = new ArrayList<>();
        int fromPage = currentPage - prevItemsCnt;
        int toPage = currentPage + nextItemsCnt;
        for (int page = fromPage; page <= toPage; page++) {
            items.add(page);
        }

        Validate.validState(items.size() <= MAX_ITEMS,
                "Size of items (%s) must be <= %d when "
                        + "totalRecords = %d, recordsPerPage = %d, currentPage = %d and totalPages = %d",
                items, MAX_ITEMS, totalRecords, recordsPerPage, currentPage, totalPages);

        return items;
    }

}