/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.util.paging; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import org.joda.beans.Bean; import org.joda.beans.BeanBuilder; import org.joda.beans.BeanDefinition; import org.joda.beans.ImmutableBean; import org.joda.beans.ImmutableConstructor; import org.joda.beans.JodaBeanUtils; import org.joda.beans.MetaProperty; import org.joda.beans.Property; import org.joda.beans.PropertyDefinition; import org.joda.beans.impl.direct.DirectFieldsBeanBuilder; import org.joda.beans.impl.direct.DirectMetaBean; import org.joda.beans.impl.direct.DirectMetaProperty; import org.joda.beans.impl.direct.DirectMetaPropertyMap; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.PublicAPI; /** * Simple immutable request for a page of results. * <p> * This class is follows the design of SQL OFFSET and FETCH/LIMIT, exposed as a first-item/size data model. * This can be used to implement traditional fixed paging or arbitrary paging starting from an index. * <p> * This class is immutable and thread-safe. */ @PublicAPI @BeanDefinition(builderScope = "private") public final class PagingRequest implements ImmutableBean, Serializable { /** * A default size for paging. */ public static final int DEFAULT_PAGING_SIZE = 20; /** * Singleton constant to request all items (no paging). */ public static final PagingRequest ALL = new PagingRequest(0, Integer.MAX_VALUE); /** * Singleton constant to request the first page of 20 items. */ public static final PagingRequest FIRST_PAGE = new PagingRequest(0, DEFAULT_PAGING_SIZE); /** * Singleton constant to request the first matching item. */ public static final PagingRequest ONE = new PagingRequest(0, 1); /** * Singleton constant to request no data, just the total count. */ public static final PagingRequest NONE = new PagingRequest(0, 0); /** * The requested first item. */ @PropertyDefinition(get = "manual") private final int _firstItem; /** * The requested number of items. */ @PropertyDefinition(get = "manual") private final int _pagingSize; //------------------------------------------------------------------------- /** * Obtains an instance based on a zero-based index and requested size. * <p> * This factory represents the internal state directly. * The index is the first item in the list of results that is required (SQL OFFSET). * The size is the requested number of items (SQL FETCH/LIMIT). * * @param index the zero-based start index, zero or greater * @param size the number of items to request, zero or greater * @return the paging request, not null * @throws IllegalArgumentException if either input is invalid */ public static PagingRequest ofIndex(int index, int size) { return new PagingRequest(index, size); } /** * Obtains an instance based on a page and paging size. * <p> * This implements paging on top of the basic first-item/size data model. * * @param page the page number, one or greater * @param pagingSize the paging size, zero or greater * @return the paging request, not null * @throws IllegalArgumentException if either input is invalid */ public static PagingRequest ofPage(int page, int pagingSize) { ArgumentChecker.notNegativeOrZero(page, "page"); ArgumentChecker.notNegative(pagingSize, "pagingSize"); int index = ((page - 1) * pagingSize); return new PagingRequest(index, pagingSize); } /** * Obtains an instance based on a page and paging size, applying default values. * <p> * This implements paging on top of the basic first-item/size data model. * The page will default to 1 if the input is 0. * The paging size will default to 20 if the input is 0. * * @param page the page number, page one chosen if zero, not negative * @param pagingSize the paging size, size twenty chosen if zero, not negative * @return the paging request, not null * @throws IllegalArgumentException if either input is negative */ public static PagingRequest ofPageDefaulted(int page, int pagingSize) { page = (page == 0 ? 1 : page); pagingSize = (pagingSize == 0 ? DEFAULT_PAGING_SIZE : pagingSize); return PagingRequest.ofPage(page, pagingSize); } /** * Creates an instance without using defaults. * <p> * A paging size of zero will only return the count of items and will * always have a first item index of zero. * * @param index the zero-based start index, zero or greater * @param size the number of items to request, zero or greater * @throws IllegalArgumentException if either input is invalid */ @ImmutableConstructor private PagingRequest(final int index, final int size) { ArgumentChecker.notNegative(index, "index"); ArgumentChecker.notNegative(size, "size"); _firstItem = (size != 0 ? index : 0); _pagingSize = size; } //------------------------------------------------------------------------- /** * Gets the first item, using a zero-based index. * <p> * In SQL this corresponds to OFFSET. * * @return the first item index, zero-based */ public int getFirstItem() { return _firstItem; } /** * Gets the requested number of items. * <p> * In SQL this corresponds to FETCH/LIMIT. * * @return the number of requested items, zero or greater */ public int getPagingSize() { return _pagingSize; } //------------------------------------------------------------------------- /** * Gets the first item, using a one-based index. * * @return the first item number, one-based */ public int getFirstItemOneBased() { return _firstItem + 1; } /** * Gets the last item exclusive, using a zero-based index. * * @return the last item index, exclusive, zero-based */ public int getLastItem() { return _firstItem + _pagingSize; } /** * Gets the last item inclusive, using a one-based index. * * @return the last item number, inclusive, one-based */ public int getLastItemOneBased() { return getLastItem(); } //------------------------------------------------------------------------- /** * Selects the elements from the list matching this request. * <p> * This will return a new list consisting of the selected elements from the supplied list. * The elements are selected based on {@link #getFirstItem()} and {@link #getLastItem()}. * * @param <T> the list type * @param list the collection to select from, not null * @return the selected list, not linked to the original, not null */ public <T> List<T> select(List<T> list) { int firstIndex = getFirstItem(); int lastIndex = getLastItem(); if (firstIndex > list.size()) { firstIndex = list.size(); } if (lastIndex > list.size()) { lastIndex = list.size(); } return new ArrayList<T>(list.subList(firstIndex, lastIndex)); } //------------------------------------------------------------------------- @Override public String toString() { return getClass().getSimpleName() + "[first=" + _firstItem + ", size=" + _pagingSize + "]"; } //------------------------- AUTOGENERATED START ------------------------- ///CLOVER:OFF /** * The meta-bean for {@code PagingRequest}. * @return the meta-bean, not null */ public static PagingRequest.Meta meta() { return PagingRequest.Meta.INSTANCE; } static { JodaBeanUtils.registerMetaBean(PagingRequest.Meta.INSTANCE); } @Override public PagingRequest.Meta metaBean() { return PagingRequest.Meta.INSTANCE; } @Override public <R> Property<R> property(String propertyName) { return metaBean().<R>metaProperty(propertyName).createProperty(this); } @Override public Set<String> propertyNames() { return metaBean().metaPropertyMap().keySet(); } //----------------------------------------------------------------------- @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj != null && obj.getClass() == this.getClass()) { PagingRequest other = (PagingRequest) obj; return (getFirstItem() == other.getFirstItem()) && (getPagingSize() == other.getPagingSize()); } return false; } @Override public int hashCode() { int hash = getClass().hashCode(); hash = hash * 31 + JodaBeanUtils.hashCode(getFirstItem()); hash = hash * 31 + JodaBeanUtils.hashCode(getPagingSize()); return hash; } //----------------------------------------------------------------------- /** * The meta-bean for {@code PagingRequest}. */ public static final class Meta extends DirectMetaBean { /** * The singleton instance of the meta-bean. */ static final Meta INSTANCE = new Meta(); /** * The meta-property for the {@code firstItem} property. */ private final MetaProperty<Integer> _firstItem = DirectMetaProperty.ofImmutable( this, "firstItem", PagingRequest.class, Integer.TYPE); /** * The meta-property for the {@code pagingSize} property. */ private final MetaProperty<Integer> _pagingSize = DirectMetaProperty.ofImmutable( this, "pagingSize", PagingRequest.class, Integer.TYPE); /** * The meta-properties. */ private final Map<String, MetaProperty<?>> _metaPropertyMap$ = new DirectMetaPropertyMap( this, null, "firstItem", "pagingSize"); /** * Restricted constructor. */ private Meta() { } @Override protected MetaProperty<?> metaPropertyGet(String propertyName) { switch (propertyName.hashCode()) { case 132704739: // firstItem return _firstItem; case 1302250925: // pagingSize return _pagingSize; } return super.metaPropertyGet(propertyName); } @Override public BeanBuilder<? extends PagingRequest> builder() { return new PagingRequest.Builder(); } @Override public Class<? extends PagingRequest> beanType() { return PagingRequest.class; } @Override public Map<String, MetaProperty<?>> metaPropertyMap() { return _metaPropertyMap$; } //----------------------------------------------------------------------- /** * The meta-property for the {@code firstItem} property. * @return the meta-property, not null */ public MetaProperty<Integer> firstItem() { return _firstItem; } /** * The meta-property for the {@code pagingSize} property. * @return the meta-property, not null */ public MetaProperty<Integer> pagingSize() { return _pagingSize; } //----------------------------------------------------------------------- @Override protected Object propertyGet(Bean bean, String propertyName, boolean quiet) { switch (propertyName.hashCode()) { case 132704739: // firstItem return ((PagingRequest) bean).getFirstItem(); case 1302250925: // pagingSize return ((PagingRequest) bean).getPagingSize(); } return super.propertyGet(bean, propertyName, quiet); } @Override protected void propertySet(Bean bean, String propertyName, Object newValue, boolean quiet) { metaProperty(propertyName); if (quiet) { return; } throw new UnsupportedOperationException("Property cannot be written: " + propertyName); } } //----------------------------------------------------------------------- /** * The bean-builder for {@code PagingRequest}. */ private static final class Builder extends DirectFieldsBeanBuilder<PagingRequest> { private int _firstItem; private int _pagingSize; /** * Restricted constructor. */ private Builder() { } //----------------------------------------------------------------------- @Override public Object get(String propertyName) { switch (propertyName.hashCode()) { case 132704739: // firstItem return _firstItem; case 1302250925: // pagingSize return _pagingSize; default: throw new NoSuchElementException("Unknown property: " + propertyName); } } @Override public Builder set(String propertyName, Object newValue) { switch (propertyName.hashCode()) { case 132704739: // firstItem this._firstItem = (Integer) newValue; break; case 1302250925: // pagingSize this._pagingSize = (Integer) newValue; break; default: throw new NoSuchElementException("Unknown property: " + propertyName); } return this; } @Override public Builder set(MetaProperty<?> property, Object value) { super.set(property, value); return this; } @Override public Builder setString(String propertyName, String value) { setString(meta().metaProperty(propertyName), value); return this; } @Override public Builder setString(MetaProperty<?> property, String value) { super.setString(property, value); return this; } @Override public Builder setAll(Map<String, ? extends Object> propertyValueMap) { super.setAll(propertyValueMap); return this; } @Override public PagingRequest build() { return new PagingRequest( _firstItem, _pagingSize); } //----------------------------------------------------------------------- @Override public String toString() { StringBuilder buf = new StringBuilder(96); buf.append("PagingRequest.Builder{"); buf.append("firstItem").append('=').append(JodaBeanUtils.toString(_firstItem)).append(',').append(' '); buf.append("pagingSize").append('=').append(JodaBeanUtils.toString(_pagingSize)); buf.append('}'); return buf.toString(); } } ///CLOVER:ON //-------------------------- AUTOGENERATED END -------------------------- }