/* * RHQ Management Platform * Copyright (C) 2005-2013 Red Hat, Inc. * All rights reserved. * * 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 version 2 of the License. * * 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 org.rhq.enterprise.server.util; import java.util.Iterator; import java.util.NoSuchElementException; import org.rhq.core.domain.criteria.BaseCriteria; import org.rhq.core.domain.util.PageControl; import org.rhq.core.domain.util.PageList; /** This class provides a way to make PageList results easily iterable with 'for each','while',etc. loops * and importantly automatically handles iteration through all PageControl results. This * means that with a CriteriaQuery instance once can do: * * for (Resource entity : query) { * * and automatically page through all of the results in PageControl.getPageSize(def. 200) chunks. * * @author John Sanda * @author Simeon Pinder * * @param <T> The return type included by the PageList. * @param <C> The Criteria subclass used to generate/execute the query. */ public class CriteriaQuery<T, C extends BaseCriteria> implements Iterable<T> { //Criteria instance used by Executor to page through results private C criteria; //Executor private CriteriaQueryExecutor<T, C> queryExecutor; /** * It is important that the <code>criteria</code> includes sorting. If not then paging is nonsensical as the DB * provides no guarantee of ordering. If no sort is specified, an implicit sort on ID is added. * * @param criteria The criteria applied to each execution of the fetch. If no sort is specified, an implicit sort on * ID is added. * @param queryExecutor */ public CriteriaQuery(C criteria, CriteriaQueryExecutor<T, C> queryExecutor) { this.criteria = criteria; this.queryExecutor = queryExecutor; } /** Returns iterator for a single page of results as defined by * i)the Criteria instance * ii)the paging details applied to the Criteria instance */ @Override public Iterator<T> iterator() { return new QueryResultsIterator(executeQuery()); } private PageList<T> executeQuery() { return queryExecutor.execute(criteria); } //Defines the iterator that: // i)creates page sized chunks results // ii)at the end of each pageList, moves the iterator to next page and continues iteration // //NOTE: Assumes criteria page iteration starts with page 0. Will continue to iterate over N members. protected class QueryResultsIterator implements Iterator<T> { private PageList<T> currentPage; private Iterator<T> iterator; private boolean reachedEnd; /**The first pageList returned by the criteria instance is where iteration begins. * @param firstPage */ public QueryResultsIterator(PageList<T> firstPage) { currentPage = firstPage; iterator = currentPage.iterator(); } @Override public boolean hasNext() { if (!iterator.hasNext() && !reachedEnd) { // advance the page. Although strange to be using a page control override in conjunction with // CriteriaQuery, nonetheless make sure we advance it if it exists, because the normal setPaging is // ignored when their is an overrides. PageControl pcCurrent = currentPage.getPageControl(); PageControl pcOverrides = criteria.getPageControlOverrides(); if (pcCurrent == null && pcOverrides == null) { // Without any indication on paging, iterate only on the initially provided list reachedEnd = true; } else { if (pcOverrides != null) { pcOverrides.setPageNumber(pcOverrides.getPageNumber() + 1); } else { criteria.setPaging(pcCurrent.getPageNumber() + 1, pcCurrent.getPageSize()); } //help out the GC. currentPage.clear(); currentPage = queryExecutor.execute(criteria); iterator = currentPage.iterator(); reachedEnd = !iterator.hasNext(); //if we got an empty collection as a result for obtaining the next page //we can be pretty sure we're past the number of available results } } return iterator.hasNext(); } @Override public T next() { if (!hasNext()) { throw new NoSuchElementException(); } return iterator.next(); } @Override public void remove() { iterator.remove(); } } }