# HG changeset patch # User smarks # Date 1431988951 25200 # Mon May 18 15:42:31 2015 -0700 # Node ID 72a34dbc0f374f3db78ef8632ea67617415dc40f # Parent 6adfb517cd7fcf6c64fd9d639d6b03e66ca5ea92 8072726: add adapter to convert Enumeration to Iterator Reviewed-by: redestad, forax, chegar, dfuchs, psandoz diff --git a/src/java.base/share/classes/java/util/Enumeration.java b/src/java.base/share/classes/java/util/Enumeration.java --- a/src/java.base/share/classes/java/util/Enumeration.java +++ b/src/java.base/share/classes/java/util/Enumeration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,11 +40,14 @@ * vector, the keys of a hashtable, and the values in a hashtable. * Enumerations are also used to specify the input streams to a * SequenceInputStream. - *

- * NOTE: The functionality of this interface is duplicated by the Iterator - * interface. In addition, Iterator adds an optional remove operation, and - * has shorter method names. New implementations should consider using - * Iterator in preference to Enumeration. + * + * @apiNote + * The functionality of this interface is duplicated by the {@link Iterator} + * interface. In addition, {@code Iterator} adds an optional remove operation, + * and has shorter method names. New implementations should consider using + * {@code Iterator} in preference to {@code Enumeration}. It is possible to + * adapt an {@code Enumeration} to an {@code Iterator} by using the + * {@link #asIterator} method. * * @see java.util.Iterator * @see java.io.SequenceInputStream @@ -76,4 +79,51 @@ * @exception NoSuchElementException if no more elements exist. */ E nextElement(); + + /** + * Returns an {@link Iterator} that traverses the remaining elements + * covered by this enumeration. Traversal is undefined if any methods + * are called on this enumeration after the call to {@code asIterator}. + * + * @apiNote + * This method is intended to help adapt code that produces + * {@code Enumeration} instances to code that consumes {@code Iterator} + * or {@code Iterable} instances. For example, the + * {@link java.util.jar.JarFile#entries JarFile.entries()} + * method returns an {@code Enumeration}. This can be adapted + * for use in an enhanced-for loop as follows: + * + *

{@code
+     *     JarFile jf = ... ;
+     *     Iterable entries = () -> jf.entries().asIterator();
+     *     for (JarEntry je : entries) {
+     *         doSomethingWithEntry(je);
+     *     }
+     * }
+ * + * Note that the lambda expression being used as an {@code Iterable} + * fetches a new {@code Enumeration} each time, so that the resulting + * {@code Iterator} instances do not interfere with each other. + * + * @implSpec + * The returned Iterator's {@link Iterator#hasNext hasNext} method calls and returns + * the value from this Enumeration's {@code hasMoreElements} method; its + * {@link Iterator#next next} method calls and returns the value from this Enumeration's + * {@code nextElement} method; and its {@link Iterator#remove remove} method throws + * {@code UnsupportedOperationException}. + * + * @return an Iterator representing the remaining elements of this Enumeration + * + * @since 1.9 + */ + default Iterator asIterator() { + return new Iterator<>() { + @Override public boolean hasNext() { + return hasMoreElements(); + } + @Override public E next() { + return nextElement(); + } + }; + } } diff --git a/src/java.base/share/classes/java/util/Iterator.java b/src/java.base/share/classes/java/util/Iterator.java --- a/src/java.base/share/classes/java/util/Iterator.java +++ b/src/java.base/share/classes/java/util/Iterator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,6 +43,10 @@ * * Java Collections Framework. * + * @apiNote + * An {@link Enumeration} can be converted into an {@code Iterator} by + * using the {@link Enumeration#asIterator} method. + * * @param the type of elements returned by this iterator * * @author Josh Bloch diff --git a/test/java/util/Collections/EnumerationAsIterator.java b/test/java/util/Collections/EnumerationAsIterator.java new file mode 100644 --- /dev/null +++ b/test/java/util/Collections/EnumerationAsIterator.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8072726 + * @summary Tests for Enumeration-to-Iterator conversion. + * @run testng EnumerationAsIterator + */ + +import java.util.*; + +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +@Test +public class EnumerationAsIterator { + public void emptyHasNext() { + Iterator it = Collections.emptyEnumeration().asIterator(); + assertFalse(it.hasNext()); + } + + @Test(expectedExceptions = {NoSuchElementException.class}) + public void emptyNextThrows() { + Iterator it = Collections.emptyEnumeration().asIterator(); + Object o = it.next(); + } + + private List copy(List input) { + List output = new ArrayList<>(); + Collections.enumeration(input) + .asIterator() + .forEachRemaining(output::add); + return output; + } + + public void copyZero() { + assertEquals(copy(Collections.emptyList()), Collections.emptyList()); + } + + public void copyOne() { + assertEquals(copy(Collections.singletonList("a")), + Collections.singletonList("a")); + } + + public void copyMany() { + List input = Arrays.asList("a", "b", "c"); + assertEquals(copy(input), input); + } + + @Test(expectedExceptions = {UnsupportedOperationException.class}) + public void removeThrowsInitially() { + Collections.enumeration(Arrays.asList("a", "b", "c")) + .asIterator() + .remove(); + } + + @Test(expectedExceptions = {UnsupportedOperationException.class}) + public void removeThrowsLater() { + Iterator iter = + Collections.enumeration(Arrays.asList("a", "b", "c")).asIterator(); + iter.next(); + iter.remove(); + } + + + public void repeatedCalls() { + Iterator iter = new Vector<>(Arrays.asList("a", "b", "c")) + .elements().asIterator(); + assertTrue(iter.hasNext()); + assertTrue(iter.hasNext()); + assertEquals(iter.next(), "a"); + assertEquals(iter.next(), "b"); + assertEquals(iter.next(), "c"); + assertFalse(iter.hasNext()); + assertFalse(iter.hasNext()); + } +}