All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kohsuke.github.GitHubPageIterator Maven / Gradle / Ivy

There is a newer version: 2.0.0-alpha-2
Show newest version
package org.kohsuke.github;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.NoSuchElementException;

import javax.annotation.Nonnull;

// TODO: Auto-generated Javadoc
/**
 * May be used for any item that has pagination information. Iterates over paginated {@link T} objects (not the items
 * inside the page). Also exposes {@link #finalResponse()} to allow getting a full {@link GitHubResponse} after
 * iterating completes.
 *
 * Works for array responses, also works for search results which are single instances with an array of items inside.
 *
 * This class is not thread-safe. Any one instance should only be called from a single thread.
 *
 * @author Liam Newman
 * @param 
 *            type of each page (not the items in the page).
 */
class GitHubPageIterator implements Iterator {

    private final GitHubClient client;
    private final Class type;

    /**
     * The page that will be returned when {@link #next()} is called.
     *
     * 

* Will be {@code null} after {@link #next()} is called. *

*

* Will not be {@code null} after {@link #fetch()} is called if a new page was fetched. *

*/ private T next; /** * The request that will be sent when to get a new response page if {@link #next} is {@code null}. Will be * {@code null} when there are no more pages to fetch. */ private GitHubRequest nextRequest; /** * When done iterating over pages, it is on rare occasions useful to be able to get information from the final * response that was retrieved. */ private GitHubResponse finalResponse = null; private GitHubPageIterator(GitHubClient client, Class type, GitHubRequest request) { if (!"GET".equals(request.method())) { throw new IllegalStateException("Request method \"GET\" is required for page iterator."); } this.client = client; this.type = type; this.nextRequest = request; } /** * Loads paginated resources. * * @param * type of each page (not the items in the page). * @param client * the {@link GitHubClient} from which to request responses * @param type * type of each page (not the items in the page). * @param request * the request * @param pageSize * the page size * @return iterator */ static GitHubPageIterator create(GitHubClient client, Class type, GitHubRequest request, int pageSize) { if (pageSize > 0) { GitHubRequest.Builder builder = request.toBuilder().with("per_page", pageSize); request = builder.build(); } return new GitHubPageIterator<>(client, type, request); } /** * {@inheritDoc} */ public boolean hasNext() { fetch(); return next != null; } /** * Gets the next page. * * @return the next page. */ @Nonnull public T next() { fetch(); T result = next; if (result == null) throw new NoSuchElementException(); // If this is the last page, keep the response next = null; return result; } /** * On rare occasions the final response from iterating is needed. * * @return the final response of the iterator. */ public GitHubResponse finalResponse() { if (hasNext()) { throw new GHException("Final response is not available until after iterator is done."); } return finalResponse; } /** * Fetch is called at the start of {@link #hasNext()} or {@link #next()} to fetch another page of data if it is * needed. *

* If {@link #next} is not {@code null}, no further action is needed. If {@link #next} is {@code null} and * {@link #nextRequest} is {@code null}, there are no more pages to fetch. *

*

* Otherwise, a new response page is fetched using {@link #nextRequest}. The response is then checked to see if * there is a page after it and {@link #nextRequest} is updated to point to it. If there are no pages available * after the current response, {@link #nextRequest} is set to {@code null}. *

*/ private void fetch() { if (next != null) return; // already fetched if (nextRequest == null) return; // no more data to fetch URL url = nextRequest.url(); try { GitHubResponse nextResponse = client.sendRequest(nextRequest, (connectorResponse) -> GitHubResponse.parseBody(connectorResponse, type)); assert nextResponse.body() != null; next = nextResponse.body(); nextRequest = findNextURL(nextRequest, nextResponse); if (nextRequest == null) { finalResponse = nextResponse; } } catch (IOException e) { // Iterators do not throw IOExceptions, so we wrap any IOException // in a runtime GHException to bubble out if needed. throw new GHException("Failed to retrieve " + url, e); } } /** * Locate the next page from the pagination "Link" tag. */ private GitHubRequest findNextURL(GitHubRequest nextRequest, GitHubResponse nextResponse) throws MalformedURLException { GitHubRequest result = null; String link = nextResponse.header("Link"); if (link != null) { for (String token : link.split(", ")) { if (token.endsWith("rel=\"next\"")) { // found the next page. This should look something like // ; rel="next" int idx = token.indexOf('>'); result = nextRequest.toBuilder().setRawUrlPath(token.substring(1, idx)).build(); break; } } } return result; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy