diff --git a/core/src/main/java/org/jclouds/collect/AdvanceUntilEmptyIterable.java b/core/src/main/java/org/jclouds/collect/AdvanceUntilEmptyIterable.java new file mode 100644 index 0000000000..309a530311 --- /dev/null +++ b/core/src/main/java/org/jclouds/collect/AdvanceUntilEmptyIterable.java @@ -0,0 +1,146 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you 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 current 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 org.jclouds.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Iterator; + +import com.google.common.annotations.Beta; +import com.google.common.base.Objects; +import com.google.common.base.Supplier; +import com.google.common.collect.AbstractIterator; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.Iterators; +import com.google.common.collect.UnmodifiableIterator; + +/** + * continues to supply iterables until the last was empty + * + * @param + */ +@Beta +public class AdvanceUntilEmptyIterable extends FluentIterable> { + + public static AdvanceUntilEmptyIterable create(Supplier> nextIterable){ + return new AdvanceUntilEmptyIterable(nextIterable); + } + + private final AdvanceUntilEmptyIterator iterator; + + protected AdvanceUntilEmptyIterable(Supplier> nextIterable) { + this.iterator = new AdvanceUntilEmptyIterator(checkNotNull(nextIterable, "next iterable")); + } + + @Override + public Iterator> iterator() { + return iterator; + } + + static class AdvanceUntilEmptyIterator extends AbstractIterator> { + + private final Supplier> nextIterable; + private transient FluentIterable current; + private transient boolean unread = true; + + AdvanceUntilEmptyIterator(Supplier> nextIterable) { + this.nextIterable = checkNotNull(nextIterable, "next iterable"); + } + + /** + * {@inheritDoc} + */ + @Override + protected FluentIterable computeNext() { + if (unread) + try { + return current = nextIterable.get(); + } finally { + unread = false; + } + else if (current.size() > 0) + return current = nextIterable.get(); + else + return endOfData(); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return Objects.hashCode(current, unread); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + AdvanceUntilEmptyIterator other = AdvanceUntilEmptyIterator.class.cast(obj); + return Objects.equal(this.current, other.current) && Objects.equal(this.unread, other.unread); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return Objects.toStringHelper("").omitNullValues().add("current", current).add("unread", unread).toString(); + } + } + + /** + * Combines all the pages into a single unmodifiable iterable. ex. + * + *
+    * FluentIterable blobs = blobstore.list(...).concat();
+    * for (StorageMetadata blob : blobs) {
+    *     process(blob);
+    * }
+    * 
+ * + * @see Iterators#concat + */ + public FluentIterable concat() { + final Iterator> iterator = iterator(); + final UnmodifiableIterator> unmodifiable = new UnmodifiableIterator>() { + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public Iterator next() { + return iterator.next().iterator(); + } + }; + return new FluentIterable() { + @Override + public Iterator iterator() { + return Iterators.concat(unmodifiable); + } + }; + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/jclouds/collect/AdvanceUntilEmptyIterableTest.java b/core/src/test/java/org/jclouds/collect/AdvanceUntilEmptyIterableTest.java new file mode 100644 index 0000000000..e3354fce81 --- /dev/null +++ b/core/src/test/java/org/jclouds/collect/AdvanceUntilEmptyIterableTest.java @@ -0,0 +1,80 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you 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 current 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 org.jclouds.collect; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; + +import org.easymock.EasyMock; +import org.testng.Assert; +import org.testng.annotations.Test; + +import com.google.common.base.Supplier; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableSet; + +/** + * Tests behavior of {@code AdvanceUntilEmptyIterable}. + * + * @author Adrian Cole + */ +@Test(testName = "AdvanceUntilEmptyIterableTest") +public class AdvanceUntilEmptyIterableTest { + + @SuppressWarnings("unchecked") + @Test + public void testSinglePageResultReturnsSame() { + + FluentIterable initial = FluentIterable.from(ImmutableSet.of("foo", "bar")); + Supplier> nextIterable = createMock(Supplier.class); + expect(nextIterable.get()).andReturn(initial); + + EasyMock.replay(nextIterable); + + AdvanceUntilEmptyIterable iterable = new AdvanceUntilEmptyIterable(nextIterable); + + Assert.assertSame(iterable.get(0), initial); + + EasyMock.verify(nextIterable); + } + + @SuppressWarnings("unchecked") + @Test + public void testConcatStopsWhenEmpty() { + + Supplier> nextIterable = createMock(Supplier.class); + + expect(nextIterable.get()).andReturn(FluentIterable.from(ImmutableSet.of("foo", "bar"))); + expect(nextIterable.get()).andReturn(FluentIterable.from(ImmutableSet.of("boo", "baz"))); + + expect(nextIterable.get()).andReturn(FluentIterable.from(ImmutableSet.of("ham", "cheeze"))); + expect(nextIterable.get()).andReturn(FluentIterable.from(ImmutableSet.of())); + + EasyMock.replay(nextIterable); + + AdvanceUntilEmptyIterable iterable = new AdvanceUntilEmptyIterable(nextIterable); + + Assert.assertEquals(iterable.concat().toImmutableSet(), + ImmutableSet.of("foo", "bar", "boo", "baz", "ham", "cheeze")); + + EasyMock.verify(nextIterable); + + } + +} diff --git a/core/src/test/java/org/jclouds/collect/PagedIterablesTest.java b/core/src/test/java/org/jclouds/collect/PagedIterablesTest.java index 9ce207dc6b..93c56dfda0 100644 --- a/core/src/test/java/org/jclouds/collect/PagedIterablesTest.java +++ b/core/src/test/java/org/jclouds/collect/PagedIterablesTest.java @@ -1,3 +1,21 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you 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 current 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 org.jclouds.collect; import static org.easymock.EasyMock.createMock;