-
-
Notifications
You must be signed in to change notification settings - Fork 724
/
PaginationAction.java
310 lines (278 loc) · 9.63 KB
/
PaginationAction.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
/*
* Copyright 2015-2017 Austin Keener & Michael Ritter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.dv8tion.jda.core.requests.restaction.pagination;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.requests.Request;
import net.dv8tion.jda.core.requests.Response;
import net.dv8tion.jda.core.requests.RestAction;
import org.apache.http.util.Args;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* {@link net.dv8tion.jda.core.requests.RestAction RestAction} specification used
* to retrieve entities for paginated endpoints (before, after, limit).
*
* @param <M>
* The current implementation used as chaining return value
* @param <T>
* The type of entity to paginate
*
* @since 3.1
* @author Florian Spieß
*/
public abstract class PaginationAction<T, M extends PaginationAction<T, M>> extends RestAction<List<T>> implements Iterable<T>
{
protected final List<T> cached = new CopyOnWriteArrayList<>();
protected final int maxLimit;
protected final int minLimit;
protected final AtomicInteger limit;
/**
* Creates a new PaginationAction instance
*
* @param api
* The current JDA instance
* @param maxLimit
* The inclusive maximum limit that can be used in {@link #limit(int)}
* @param minLimit
* The inclusive minimum limit that can be used in {@link #limit(int)}
* @param initialLimit
* The initial limit to use on the pagination endpoint
*/
public PaginationAction(JDA api, int minLimit, int maxLimit, int initialLimit)
{
super(api, null, null);
this.maxLimit = maxLimit;
this.minLimit = minLimit;
this.limit = new AtomicInteger(initialLimit);
}
/**
* Creates a new PaginationAction instance
* <br>This is used for PaginationActions that should not deal with
* {@link #limit(int)}
*
* @param api
* The current JDA instance
*/
public PaginationAction(JDA api)
{
super(api, null, null);
this.maxLimit = 0;
this.minLimit = 0;
this.limit = new AtomicInteger(0);
}
/**
* The current amount of cached entities for this PaginationAction
*
* @return int size of currently cached entities
*/
public int cacheSize()
{
return cached.size();
}
/**
* Whether the cache of this PaginationAction is empty.
* <br>Logically equivalent to {@code cacheSize() == 0}.
*
* @return True, if no entities have been retrieved yet.
*/
public boolean isEmpty()
{
return cached.isEmpty();
}
/**
* The currently cached entities of recent execution tasks.
* <br>Every {@link net.dv8tion.jda.core.requests.RestAction RestAction} success
* adds to this List. (Thread-Safe due to {@link java.util.concurrent.CopyOnWriteArrayList CopyOnWriteArrayList})
*
* <p><b>This <u>does not</u> contain all entities for the paginated endpoint unless the pagination has reached an end!</b>
* <br>It only contains those entities which already have been retrieved.
*
* @return Immutable {@link java.util.List List} containing all currently cached entities for this PaginationAction
*/
public List<T> getCached()
{
return Collections.unmodifiableList(cached);
}
/**
* The most recent entity retrieved by this PaginationAction instance
*
* @throws java.util.NoSuchElementException
* If no entities have been retrieved yet (see {@link #isEmpty()})
*
* @return The most recent cached entity
*/
public T getLast()
{
if (cached.isEmpty())
throw new NoSuchElementException("No entities have been retrieved yet.");
return cached.get(cached.size() - 1);
}
/**
* The first cached entity retrieved by this PaginationAction instance
*
* @throws java.util.NoSuchElementException
* If no entities have been retrieved yet (see {@link #isEmpty()})
*
* @return The very first cached entity
*/
public T getFirst()
{
if (cached.isEmpty())
throw new NoSuchElementException("No entities have been retrieved yet.");
return cached.get(0);
}
/**
* Sets the limit that should be used in the next RestAction completion
* call or by the next iterator retrieve call.
*
* @param limit
* The limit to use
*
* @throws java.lang.IllegalArgumentException
* If the provided limit is out of range
*
* @return The current PaginationAction implementation instance
*/
public M limit(int limit)
{
Args.check(maxLimit == 0 || limit <= maxLimit, "Limit must not exceed %d!", maxLimit);
Args.check(minLimit == 0 || limit >= minLimit, "Limit must be greater or equal to %d", minLimit);
this.limit.set(limit);
return (M) this;
}
/**
* The maximum limit that can be used for this PaginationAction
* <br>Limits provided to {@link #limit(int)} must not be greater
* than the returned value.
* <br>If no maximum limit is used this will return {@code 0}.
* That means there is no upper border for limiting this PaginationAction
*
* @return The maximum limit
*/
public final int getMaxLimit()
{
return maxLimit;
}
/**
* The minimum limit that can be used for this PaginationAction
* <br>Limits provided to {@link #limit(int)} must not be less
* than the returned value.
* <br>If no minimum limit is used this will return {@code 0}.
* That means there is no lower border for limiting this PaginationAction
*
* @return The minimum limit
*/
public final int getMinLimit()
{
return minLimit;
}
/**
* The currently used limit.
* <br>If this PaginationAction does not use limitation
* this will return {@code 0}
*
* @return limit
*/
public final int getLimit()
{
return limit.get();
}
/**
* {@link net.dv8tion.jda.core.requests.restaction.pagination.PaginationAction.PaginationIterator PaginationIterator}
* that will iterate over all entities for this PaginationAction.
*
* @return new PaginationIterator
*/
@Override
public PaginationIterator iterator()
{
return new PaginationIterator();
}
@Override
public Spliterator<T> spliterator()
{
return Spliterators.spliteratorUnknownSize(iterator(), Spliterator.IMMUTABLE);
}
/**
* A sequential {@link java.util.stream.Stream Stream} with this PaginationAction as its source.
*
* @return a sequential {@code Stream} over the elements in this PaginationAction
*/
public Stream<T> stream()
{
return StreamSupport.stream(spliterator(), false);
}
/**
* Returns a possibly parallel {@link java.util.stream.Stream Stream} with this PaginationAction as its
* source. It is allowable for this method to return a sequential stream.
*
* @return a sequential {@code Stream} over the elements in this PaginationAction
*/
public Stream<T> parallelStream()
{
return StreamSupport.stream(spliterator(), true);
}
protected abstract void finalizeRoute();
protected abstract void handleResponse(Response response, Request<List<T>> request);
/**
* Iterator implementation for a {@link net.dv8tion.jda.core.requests.restaction.pagination.PaginationAction PaginationAction}.
* <br>This iterator will first iterate over all currently cached entities and continue to retrieve new entities
* as needed.
*
* <p>To retrieve new entities after reaching the end of the current cache, this iterator will
* request a List of new entities through a call of {@link net.dv8tion.jda.core.requests.RestAction#complete() RestAction.complete()}.
* <br><b>It is recommended to use the highest possible limit for this task. (see {@link #limit(int)})</b>
*/
public class PaginationIterator implements Iterator<T>
{
protected int current = 0;
@Override
public boolean hasNext()
{
if (current < 0)
return false;
if (hitEnd())
{
synchronized (limit)
{
final int tmp = limit.getAndSet(maxLimit);
complete();
limit.set(tmp);
}
if (!hitEnd())
return true;
// -1 indicates that the real end has been reached
current = -1;
return false;
}
return true;
}
@Override
public T next()
{
if (!hasNext())
throw new NoSuchElementException("Reached End of pagination task!");
return cached.get(current++);
}
protected boolean hitEnd()
{
return current < 0 || current >= cached.size();
}
}
}