001/*
002 * Copyright 2020 Global Biodiversity Information Facility (GBIF)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.gbif.api.util.iterables;
017
018import org.gbif.api.model.common.paging.PagingRequest;
019import org.gbif.api.model.common.paging.PagingResponse;
020
021import java.util.Iterator;
022
023import org.slf4j.Logger;
024import org.slf4j.LoggerFactory;
025
026import static org.gbif.api.util.PreconditionUtils.checkArgument;
027
028/**
029 * Iterator over registry entities from paging responses that filters out deleted entities.
030 */
031public abstract class BasePager<T> implements Iterable<T> {
032    private static final Logger LOG = LoggerFactory.getLogger(BasePager.class);
033    private final int pageSize;
034
035    /**
036     * @param pageSize to use when talking to the registry
037     */
038    public BasePager(int pageSize) {
039        checkArgument(pageSize > 0, "pageSize must at least be 1");
040        this.pageSize = pageSize;
041    }
042
043    class ResponseIterator implements Iterator<T>{
044        private final PagingRequest page = new PagingRequest(0, pageSize);
045        private PagingResponse<T> resp = null;
046        private Iterator<T> iter;
047        private T next;
048
049        public ResponseIterator() {
050            loadPage();
051            next = nextEntity();
052        }
053
054        @Override
055        public boolean hasNext() {
056            return next != null;
057        }
058
059        @Override
060        public T next() {
061            T entity = next;
062            next = nextEntity();
063            return entity;
064        }
065
066        @Override
067        public void remove() {
068            throw new UnsupportedOperationException();
069        }
070
071        private T nextEntity() {
072            while (true) {
073                if (!iter.hasNext()) {
074                    if (resp.isEndOfRecords()) {
075                        // no more records to load, stop!
076                        return null;
077                    } else {
078                        loadPage();
079                    }
080                }
081                T entity = iter.next();
082                if (!exclude(entity)) {
083                    return entity;
084                }
085            }
086        }
087
088        private void loadPage() {
089            LOG.debug("Loading page {}-{}", page.getOffset(), page.getOffset() + page.getLimit());
090            resp = nextPage(page);
091            iter = resp.getResults().iterator();
092            page.nextPage();
093        }
094    }
095
096    public abstract PagingResponse<T> nextPage(PagingRequest page);
097
098    /**
099     * Override this method to implement other exclusion filters.
100     */
101    protected boolean exclude(T entity) {
102        return false;
103    }
104
105    @Override
106    public Iterator<T> iterator() {
107        return new ResponseIterator();
108    }
109
110}