001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.objecttools;
018
019import static java.util.Arrays.*;
020
021import java.lang.reflect.*;
022import java.util.*;
023import java.util.ArrayList;
024
025import org.apache.juneau.*;
026
027/**
028 * POJO model paginator.
029 *
030 * <p>
031 *    This class is designed to extract sublists from arrays/collections of maps or beans.
032 * </p>
033 *
034 * <h5 class='section'>Example:</h5>
035 * <p class='bjava'>
036 *    MyBean[] <jv>arrayOfBeans</jv> = ...;
037 *    ObjectPaginator <jv>paginator</jv> = ObjectPaginator.<jsm>create</jsm>();
038 *
039 *    <jc>// Returns all rows from 100 to 110.</jc>
040 *    List&lt;MyBean&gt; <jv>result</jv> = <jv>paginator</jv>.run(<jv>arrayOfBeans</jv>, 100, 10);
041 * </p>
042 * <p>
043 *    The tool can be used against the following data types:
044 * </p>
045 * <ul>
046 *    <li>Arrays/collections of maps or beans.
047 * </ul>
048 *
049 * <h5 class='section'>See Also:</h5><ul>
050 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ObjectTools">Object Tools</a>
051
052 * </ul>
053 */
054public class ObjectPaginator implements ObjectTool<PageArgs> {
055   /**
056    * Static creator.
057    * @return A new {@link ObjectPaginator} object.
058    */
059   public static ObjectPaginator create() {
060      return new ObjectPaginator();
061   }
062
063   @Override /* Overridden from ObjectTool */
064   @SuppressWarnings({ "rawtypes", "unchecked" })
065   public Object run(BeanSession session, Object input, PageArgs args) {
066
067      if (input == null)
068         return null;
069
070      var type = session.getClassMetaForObject(input);
071
072      if (! type.isCollectionOrArray())
073         return input;
074
075      int pos = args.getPosition();
076      int limit = args.getLimit();
077
078      if (type.isArray()) {
079         var size = Array.getLength(input);
080         var end = (limit + pos >= size) ? size : limit + pos;
081         pos = Math.min(pos, size);
082         var et = type.getElementType();
083         if (! et.isPrimitive())
084            return copyOfRange((Object[])input, pos, end);
085         if (et.is(boolean.class))
086            return copyOfRange((boolean[])input, pos, end);
087         if (et.is(byte.class))
088            return copyOfRange((byte[])input, pos, end);
089         if (et.is(char.class))
090            return copyOfRange((char[])input, pos, end);
091         if (et.is(double.class))
092            return copyOfRange((double[])input, pos, end);
093         if (et.is(float.class))
094            return copyOfRange((float[])input, pos, end);
095         if (et.is(int.class))
096            return copyOfRange((int[])input, pos, end);
097         if (et.is(long.class))
098            return copyOfRange((long[])input, pos, end);
099         return copyOfRange((short[])input, pos, end);
100      }
101
102      var l = type.isList() ? (List)input : new ArrayList((Collection)input);
103      var end = (limit + pos >= l.size()) ? l.size() : limit + pos;
104      pos = Math.min(pos, l.size());
105      return l.subList(pos, end);
106   }
107
108   /**
109    * Convenience method for executing the paginator.
110    *
111    * @param <R> The collection element type.
112    * @param input The input.  Must be a collection or array of objects.
113    * @param pos The zero-index position to start from.
114    * @param limit The max number of entries to retrieve.
115    * @return A sublist of representing the entries from the position with the specified limit.
116    */
117   @SuppressWarnings("unchecked")
118   public <R> List<R> run(Object input, int pos, int limit) {
119      var bs = BeanContext.DEFAULT_SESSION;
120      var r = run(BeanContext.DEFAULT_SESSION, input, PageArgs.create(pos, limit));
121      if (r instanceof List r2)
122         return r2;
123      return bs.convertToType(r, List.class);
124   }
125}