1 package com.atlassian.marketplace.client.api;
2
3 import java.util.Iterator;
4
5 import com.atlassian.fugue.Option;
6
7 import com.google.common.collect.ImmutableList;
8 import com.google.common.collect.Iterables;
9
10 import static com.atlassian.fugue.Option.none;
11 import static com.google.common.base.Preconditions.checkNotNull;
12 import static com.google.common.collect.Iterables.isEmpty;
13
14 /**
15 * A subset of items returned by a query. The maximum size of the subset is determined by the
16 * <tt>limit</tt> parameter used in the query, or by the server's maximum result set size,
17 * whichever is lower.
18 * <p>
19 * The items themselves are accessed by treating the page as an Iterable.
20 * <p>
21 * To get the next or previous page of query results, if any, pass a {@link PageReference}
22 * obtained from {@link #getNext()} or {@link #getPrevious()} to the client's generic
23 * {@link com.atlassian.marketplace.client.MarketplaceClient#getMore} method.
24 * for the type of items. For instance, for plugin listings:
25 * <pre>
26 * Page<AddonSummary> aPage = client.addons().find(addonQuery);
27 * if (aPage.getNext().isDefined())
28 * {
29 * for (PageReference<AddonSummary> next: aPage.getNext())
30 * {
31 * Page<AddonSummary> nextPage = client.getMore(next);
32 * }
33 * }
34 * </pre>
35 */
36 public abstract class Page<T> implements Iterable<T>
37 {
38 private final ImmutableList<T> items;
39 private final int totalSize;
40 protected final PageReader<T> reader;
41
42 /**
43 * Returns a Page containing no items, with no server URI ({@link #getReference()}, {@link #getNext()},
44 * and {@link #getPrevious()} will all return {@link Option#none()}).
45 */
46 @SuppressWarnings("unchecked")
47 public static <T> Page<T> empty()
48 {
49 return (Page<T>) EMPTY_PAGE;
50 }
51
52 /**
53 * Returns a Page containing no items, with no server URI ({@link #getReference()}, {@link #getNext()},
54 * and {@link #getPrevious()} will all return {@link Option#none()}).
55 */
56 @SuppressWarnings("unchecked")
57 public static <T> Page<T> empty(final Class<T> type)
58 {
59 return (Page<T>) EMPTY_PAGE;
60 }
61
62 /**
63 * Returns a Page from a fixed list of items, with no server URI ({@link #getReference()},
64 * {@link #getNext()}, and {@link #getPrevious()} will all return {@link Option#none()}).
65 */
66 public static <T> Page<T> fromItems(Iterable<T> items)
67 {
68 return isEmpty(items) ? Page.<T>empty() : new FixedPage<T>(items);
69 }
70
71 protected Page(Iterable<T> items, int totalSize, PageReader<T> reader)
72 {
73 this.items = ImmutableList.copyOf(checkNotNull(items, "items"));
74 this.totalSize = totalSize;
75 this.reader = checkNotNull(reader);
76 }
77
78 @Override
79 public Iterator<T> iterator()
80 {
81 return items.iterator();
82 }
83
84 /**
85 * The number of items on this page.
86 */
87 public int size()
88 {
89 return items.size();
90 }
91
92 /**
93 * The number of items in the entire result set, of which this page is a subset.
94 */
95 public int totalSize()
96 {
97 return totalSize;
98 }
99
100 /**
101 * Returns a reference to the address of this query page on the server allowing it to be requeried
102 * in the future, or {@link Option#none()} if it does not exist on the server.
103 */
104 public abstract Option<PageReference<T>> getReference();
105
106 /**
107 * If there are other items before this subset in the full result set, returns a {@link PageReference}
108 * allowing you to query the previous page; otherwise returns {@link Option#none()}.
109 */
110 public abstract Option<PageReference<T>> getPrevious();
111
112 /**
113 * If there are other items after this subset in the full result set, returns a {@link PageReference}
114 * allowing you to query the next page; otherwise returns {@link Option#none()}.
115 */
116 public abstract Option<PageReference<T>> getNext();
117
118 /**
119 * Shortcut for <tt>getReference().get().getOffset()</tt>, but returns zero if <tt>getReference()</tt>
120 * is <tt>none()</tt>.
121 */
122 public int getOffset()
123 {
124 for (PageReference<T> ref: getReference())
125 {
126 return ref.getBounds().getOffset();
127 }
128 return 0;
129 }
130
131 private static final Page<Object> EMPTY_PAGE = new FixedPage<Object>(ImmutableList.<Object>of());
132
133 private static final class FixedPage<T> extends Page<T>
134 {
135 FixedPage(Iterable<T> items)
136 {
137 super(ImmutableList.copyOf(items), Iterables.size(items), PageReader.<T>stub());
138 }
139
140 @Override
141 public Option<PageReference<T>> getReference()
142 {
143 return none();
144 }
145
146 @Override
147 public Option<PageReference<T>> getPrevious()
148 {
149 return none();
150 }
151
152 @Override
153 public Option<PageReference<T>> getNext()
154 {
155 return none();
156 }
157 };
158 }