diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/Page.java b/gcloud-java-core/src/main/java/com/google/gcloud/Page.java
index 1b7754562716..2819b56a17a0 100644
--- a/gcloud-java-core/src/main/java/com/google/gcloud/Page.java
+++ b/gcloud-java-core/src/main/java/com/google/gcloud/Page.java
@@ -16,11 +16,22 @@
package com.google.gcloud;
+import java.util.Iterator;
+
/**
* Interface for Google Cloud paginated results.
*
*
- * A typical {@code Page} usage:
+ * Use {@code Page} to iterate through all values (also in next pages):
+ *
{@code
+ * Page page = ...; // get a Page instance
+ * Iterator iterator = page.iterateAll();
+ * while (iterator.hasNext()) {
+ * T value = iterator.next();
+ * // do something with value
+ * }}
+ *
+ * Or handle pagination explicitly:
*
{@code
* Page page = ...; // get a Page instance
* while (page != null) {
@@ -28,8 +39,7 @@
* // do something with value
* }
* page = page.nextPage();
- * }
- * }
+ * }}
*/
public interface Page {
@@ -38,6 +48,12 @@ public interface Page {
*/
Iterable values();
+ /**
+ * Returns an iterator for all values, possibly also in the next pages. Once current page's values
+ * are traversed the iterator fetches next page, if any.
+ */
+ Iterator iterateAll();
+
/**
* Returns the cursor for the nextPage or {@code null} if no more results.
*/
diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/PageImpl.java b/gcloud-java-core/src/main/java/com/google/gcloud/PageImpl.java
index 3925079c8d4b..a3fcb26b30cd 100644
--- a/gcloud-java-core/src/main/java/com/google/gcloud/PageImpl.java
+++ b/gcloud-java-core/src/main/java/com/google/gcloud/PageImpl.java
@@ -18,6 +18,8 @@
import java.io.Serializable;
import java.util.Collections;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
import java.util.Objects;
/**
@@ -35,6 +37,50 @@ public interface NextPageFetcher extends Serializable {
Page nextPage();
}
+ static class PageIterator implements java.util.Iterator {
+
+ private Iterator currentPageIterator;
+ private Page currentPage;
+
+ PageIterator(Page currentPage) {
+ this.currentPageIterator = currentPage.values().iterator();
+ this.currentPage = currentPage;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (currentPageIterator.hasNext()) {
+ return true;
+ }
+ Page nextPage = currentPage.nextPage();
+ if (nextPage != null) {
+ currentPage = nextPage;
+ currentPageIterator = currentPage.values().iterator();
+ return currentPageIterator.hasNext();
+ }
+ return false;
+ }
+
+ @Override
+ public T next() {
+ if (currentPageIterator.hasNext()) {
+ return currentPageIterator.next();
+ }
+ Page nextPage = currentPage.nextPage();
+ if (nextPage != null) {
+ currentPage = nextPage;
+ currentPageIterator = currentPage.values().iterator();
+ return currentPageIterator.next();
+ }
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("Can not call remove on page iterator");
+ }
+ }
+
/**
* Creates a {@code PageImpl} object. In order for the object to be serializable the {@code
* results} parameter must be serializable.
@@ -50,6 +96,11 @@ public Iterable values() {
return results == null ? Collections.EMPTY_LIST : results;
}
+ @Override
+ public Iterator iterateAll() {
+ return new PageIterator(this);
+ }
+
@Override
public String nextPageCursor() {
return cursor;
diff --git a/gcloud-java-core/src/test/java/com/google/gcloud/PageImplTest.java b/gcloud-java-core/src/test/java/com/google/gcloud/PageImplTest.java
index 78aa3feaa281..fb289186de8d 100644
--- a/gcloud-java-core/src/test/java/com/google/gcloud/PageImplTest.java
+++ b/gcloud-java-core/src/test/java/com/google/gcloud/PageImplTest.java
@@ -26,21 +26,38 @@
public class PageImplTest {
+ private static final ImmutableList VALUES = ImmutableList.of("1", "2");
+ private static final ImmutableList NEXT_VALUES = ImmutableList.of("3", "4");
+ private static final ImmutableList ALL_VALUES = ImmutableList.builder()
+ .addAll(VALUES)
+ .addAll(NEXT_VALUES)
+ .build();
+
@Test
- public void testPage() throws Exception {
- ImmutableList values = ImmutableList.of("1", "2");
- final PageImpl nextResult =
- new PageImpl<>(null, "c", Collections.emptyList());
+ public void testPage() {
+ final PageImpl nextResult = new PageImpl<>(null, "c", NEXT_VALUES);
PageImpl.NextPageFetcher fetcher = new PageImpl.NextPageFetcher() {
-
@Override
public PageImpl nextPage() {
return nextResult;
}
};
- PageImpl result = new PageImpl<>(fetcher, "c", values);
+ PageImpl result = new PageImpl<>(fetcher, "c", VALUES);
assertEquals(nextResult, result.nextPage());
assertEquals("c", result.nextPageCursor());
- assertEquals(values, ImmutableList.copyOf(result.values().iterator()));
+ assertEquals(VALUES, result.values());
+ }
+
+ @Test
+ public void testIterateAll() {
+ final PageImpl nextResult = new PageImpl<>(null, "c", NEXT_VALUES);
+ PageImpl.NextPageFetcher fetcher = new PageImpl.NextPageFetcher() {
+ @Override
+ public PageImpl nextPage() {
+ return nextResult;
+ }
+ };
+ PageImpl result = new PageImpl<>(fetcher, "c", VALUES);
+ assertEquals(ALL_VALUES, ImmutableList.copyOf(result.iterateAll()));
}
}
diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java b/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java
index 7cf7fe2454fc..b7a36de7589e 100644
--- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java
+++ b/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java
@@ -53,6 +53,7 @@
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -214,12 +215,9 @@ String parse(String... args) {
public void run(Storage storage, String bucketName) {
if (bucketName == null) {
// list buckets
- Page bucketPage = storage.list();
- while (bucketPage != null) {
- for (BucketInfo b : bucketPage.values()) {
- System.out.println(b);
- }
- bucketPage = bucketPage.nextPage();
+ Iterator bucketInfoIterator = storage.list().iterateAll();
+ while (bucketInfoIterator.hasNext()) {
+ System.out.println(bucketInfoIterator.next());
}
} else {
// list a bucket's blobs
@@ -228,12 +226,9 @@ public void run(Storage storage, String bucketName) {
System.out.println("No such bucket");
return;
}
- Page blobPage = bucket.list();
- while (blobPage != null) {
- for (Blob b : blobPage.values()) {
- System.out.println(b.info());
- }
- blobPage = blobPage.nextPage();
+ Iterator blobIterator = bucket.list().iterateAll();
+ while (blobIterator.hasNext()) {
+ System.out.println(blobIterator.next().info());
}
}
}
diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/RemoteGcsHelperTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/RemoteGcsHelperTest.java
index ff6fd68fd1eb..47d8e8d433d2 100644
--- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/RemoteGcsHelperTest.java
+++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/RemoteGcsHelperTest.java
@@ -33,6 +33,7 @@
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
+import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
@@ -90,6 +91,11 @@ public Page nextPage() {
public Iterable values() {
return BLOB_LIST;
}
+
+ @Override
+ public Iterator iterateAll() {
+ return null;
+ }
};
private static String keyPath = "/does/not/exist/key." + UUID.randomUUID().toString() + ".json";