PagedIterable.java
package org.kohsuke.github;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nonnull;
// TODO: Auto-generated Javadoc
/**
* {@link Iterable} that returns {@link PagedIterator}. {@link PagedIterable} is thread-safe but {@link PagedIterator}
* is not. Any one instance of {@link PagedIterator} should only be called from a single thread.
*
* @author Kohsuke Kawaguchi
* @param <T>
* the type of items on each page
*/
public abstract class PagedIterable<T> implements Iterable<T> {
/**
* Page size. 0 is default.
*/
private int pageSize = 0;
/**
* Instantiate a PagedIterable.
*/
public PagedIterable() {
}
/**
* Sets the pagination size.
*
* <p>
* When set to non-zero, each API call will retrieve this many entries.
*
* @param size
* the size
* @return the paged iterable
*/
public PagedIterable<T> withPageSize(int size) {
this.pageSize = size;
return this;
}
/**
* Returns an iterator over elements of type {@code T}.
*
* @return an Iterator.
*/
@Nonnull
public final PagedIterator<T> iterator() {
return _iterator(pageSize);
}
/**
* Iterator over page items.
*
* @param pageSize
* the page size
* @return the paged iterator
*/
@Nonnull
public abstract PagedIterator<T> _iterator(int pageSize);
/**
* Eagerly walk {@link PagedIterator} and return the result in an array.
*
* @param iterator
* the {@link PagedIterator} to read
* @return an array of all elements from the {@link PagedIterator}
* @throws IOException
* if an I/O exception occurs.
*/
protected T[] toArray(final PagedIterator<T> iterator) throws IOException {
try {
ArrayList<T[]> pages = new ArrayList<>();
int totalSize = 0;
T[] item;
do {
item = iterator.nextPageArray();
totalSize += Array.getLength(item);
pages.add(item);
} while (iterator.hasNext());
Class<T[]> type = (Class<T[]>) item.getClass();
return concatenatePages(type, pages, totalSize);
} catch (GHException e) {
// if there was an exception inside the iterator it is wrapped as a GHException
// if the wrapped exception is an IOException, throw that
if (e.getCause() instanceof IOException) {
throw (IOException) e.getCause();
} else {
throw e;
}
}
}
/**
* Eagerly walk {@link Iterable} and return the result in an array.
*
* @return the list
* @throws IOException
* if an I/O exception occurs.
*/
@Nonnull
public T[] toArray() throws IOException {
return toArray(iterator());
}
/**
* Eagerly walk {@link Iterable} and return the result in a list.
*
* @return the list
* @throws IOException
* if an I/O Exception occurs
*/
@Nonnull
public List<T> toList() throws IOException {
return Collections.unmodifiableList(Arrays.asList(this.toArray()));
}
/**
* Eagerly walk {@link Iterable} and return the result in a set.
*
* @return the set
* @throws IOException
* if an I/O Exception occurs
*/
@Nonnull
public Set<T> toSet() throws IOException {
return Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(this.toArray())));
}
/**
* Concatenates a list of arrays into a single array.
*
* @param type
* the type of array to be returned.
* @param pages
* the list of arrays to be concatenated.
* @param totalLength
* the total length of the returned array.
* @return an array containing all elements from all pages.
*/
@Nonnull
private T[] concatenatePages(Class<T[]> type, List<T[]> pages, int totalLength) {
T[] result = type.cast(Array.newInstance(type.getComponentType(), totalLength));
int position = 0;
for (T[] page : pages) {
final int pageLength = Array.getLength(page);
System.arraycopy(page, 0, result, position, pageLength);
position += pageLength;
}
return result;
}
}