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   //-----------------------------------------------------------------------------------------------------------------
057   // Static
058   //-----------------------------------------------------------------------------------------------------------------
059
060   /**
061    * Static creator.
062    * @return A new {@link ObjectPaginator} object.
063    */
064   public static ObjectPaginator create() {
065      return new ObjectPaginator();
066   }
067
068   //-----------------------------------------------------------------------------------------------------------------
069   // Instance
070   //-----------------------------------------------------------------------------------------------------------------
071
072   /**
073    * Convenience method for executing the paginator.
074    *
075    * @param <R> The collection element type.
076    * @param input The input.  Must be a collection or array of objects.
077    * @param pos The zero-index position to start from.
078    * @param limit The max number of entries to retrieve.
079    * @return A sublist of representing the entries from the position with the specified limit.
080    */
081   @SuppressWarnings("unchecked")
082   public <R> List<R> run(Object input, int pos, int limit) {
083      BeanSession bs = BeanContext.DEFAULT_SESSION;
084      Object r = run(BeanContext.DEFAULT_SESSION, input, PageArgs.create(pos, limit));
085      if (r instanceof List)
086         return (List<R>)r;
087      return bs.convertToType(r, List.class);
088   }
089
090   @Override /* ObjectTool */
091   @SuppressWarnings({ "rawtypes", "unchecked" })
092   public Object run(BeanSession session, Object input, PageArgs args) {
093
094      if (input == null)
095         return null;
096
097      ClassMeta type = session.getClassMetaForObject(input);
098
099      if (! type.isCollectionOrArray())
100         return input;
101
102      int pos = args.getPosition();
103      int limit = args.getLimit();
104
105      if (type.isArray()) {
106         int size = Array.getLength(input);
107         int end = (limit+pos >= size) ? size : limit + pos;
108         pos = Math.min(pos, size);
109         ClassMeta<?> et = type.getElementType();
110         if (! et.isPrimitive())
111            return copyOfRange((Object[])input, pos, end);
112         if (et.is(boolean.class))
113            return copyOfRange((boolean[])input, pos, end);
114         if (et.is(byte.class))
115            return copyOfRange((byte[])input, pos, end);
116         if (et.is(char.class))
117            return copyOfRange((char[])input, pos, end);
118         if (et.is(double.class))
119            return copyOfRange((double[])input, pos, end);
120         if (et.is(float.class))
121            return copyOfRange((float[])input, pos, end);
122         if (et.is(int.class))
123            return copyOfRange((int[])input, pos, end);
124         if (et.is(long.class))
125            return copyOfRange((long[])input, pos, end);
126         return copyOfRange((short[])input, pos, end);
127      }
128
129      List l = type.isList() ? (List)input : new ArrayList((Collection)input);
130      int end = (limit+pos >= l.size()) ? l.size() : limit + pos;
131      pos = Math.min(pos, l.size());
132      return l.subList(pos, end);
133   }
134}