From 7eeafb719f5b87afa575a8d6a1452dcbc87ff4e6 Mon Sep 17 00:00:00 2001 From: Martin Buchholz Date: Mon, 25 Jun 2018 18:01:42 -0700 Subject: [PATCH] 8205184: Delegating Iterator implementations that don't delegate forEachRemaining() Co-authored-by: Tobias Thierer Reviewed-by: psandoz --- .../share/classes/java/util/Collections.java | 18 +- .../DelegatingIteratorForEachRemaining.java | 198 ++++++++++++++++++ 2 files changed, 213 insertions(+), 3 deletions(-) create mode 100644 test/jdk/java/util/Collections/DelegatingIteratorForEachRemaining.java diff --git a/src/java.base/share/classes/java/util/Collections.java b/src/java.base/share/classes/java/util/Collections.java index 6842d7e4aca..d1fb457cc97 100644 --- a/src/java.base/share/classes/java/util/Collections.java +++ b/src/java.base/share/classes/java/util/Collections.java @@ -1569,7 +1569,8 @@ public class Collections { super((Set)s); } - static Consumer> entryConsumer(Consumer> action) { + static Consumer> entryConsumer( + Consumer> action) { return e -> action.accept(new UnmodifiableEntry<>(e)); } @@ -1661,6 +1662,9 @@ public class Collections { public void remove() { throw new UnsupportedOperationException(); } + public void forEachRemaining(Consumer> action) { + i.forEachRemaining(entryConsumer(action)); + } }; } @@ -3081,7 +3085,11 @@ public class Collections { return new Iterator() { public boolean hasNext() { return it.hasNext(); } public E next() { return it.next(); } - public void remove() { it.remove(); }}; + public void remove() { it.remove(); } + public void forEachRemaining(Consumer action) { + it.forEachRemaining(action); + } + }; } public boolean add(E e) { return c.add(typeCheck(e)); } @@ -3757,7 +3765,6 @@ public class Collections { public Iterator> iterator() { final Iterator> i = s.iterator(); - final Class valueType = this.valueType; return new Iterator>() { public boolean hasNext() { return i.hasNext(); } @@ -3766,6 +3773,11 @@ public class Collections { public Map.Entry next() { return checkedEntry(i.next(), valueType); } + + public void forEachRemaining(Consumer> action) { + i.forEachRemaining( + e -> action.accept(checkedEntry(e, valueType))); + } }; } diff --git a/test/jdk/java/util/Collections/DelegatingIteratorForEachRemaining.java b/test/jdk/java/util/Collections/DelegatingIteratorForEachRemaining.java new file mode 100644 index 00000000000..65bd0b943dd --- /dev/null +++ b/test/jdk/java/util/Collections/DelegatingIteratorForEachRemaining.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2018 Google Inc. 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 + * @run junit DelegatingIteratorForEachRemaining + */ + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.Spliterator; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; + +public class DelegatingIteratorForEachRemaining { + + static abstract class ForwardingIterator implements Iterator { + private final Iterator delegate; + + protected ForwardingIterator(Iterator delegate) { + this.delegate = Objects.requireNonNull(delegate); + } + + @Override public boolean hasNext() { return delegate.hasNext(); } + @Override public E next() { return delegate.next(); } + @Override public void remove() { delegate.remove(); } + @Override public void forEachRemaining(Consumer action) { + delegate.forEachRemaining(action); + } + } + + static final class ThrowingIterator extends ForwardingIterator { + public ThrowingIterator(Iterator delegate) { + super(delegate); + } + + @Override + public void forEachRemaining(Consumer action) { + throw new UnsupportedOperationException(); + } + } + + static abstract class ForwardingSet implements Set { + private final Set delegate; + + protected ForwardingSet(Set delegate) { + this.delegate = Objects.requireNonNull(delegate); + } + + @Override public int size() { return delegate.size(); } + @Override public boolean isEmpty() { return delegate.isEmpty(); } + @Override public boolean contains(Object o) { return delegate.contains(o); } + @Override public Iterator iterator() { return delegate.iterator(); } + @Override public Object[] toArray() { return delegate.toArray(); } + @Override public T[] toArray( T[] ts) { return delegate.toArray(ts); } + @Override public boolean add(E e) { return delegate.add(e); } + @Override public boolean remove(Object o) { return delegate.remove(o); } + @Override public boolean containsAll( Collection c) { return delegate.containsAll(c); } + @Override public boolean addAll( Collection c) { return delegate.addAll(c); } + @Override public boolean retainAll( Collection c) { return delegate.retainAll(c); } + @Override public boolean removeAll( Collection c) { return delegate.removeAll(c); } + @Override public void clear() { delegate.clear(); } + @Override public boolean equals(Object o) { return delegate.equals(o); } + @Override public int hashCode() { return delegate.hashCode(); } + @Override public Spliterator spliterator() { return delegate.spliterator(); } + @Override public boolean removeIf(Predicate filter) { return delegate.removeIf(filter); } + @Override public Stream stream() { return delegate.stream(); } + @Override public Stream parallelStream() { return delegate.parallelStream(); } + @Override public void forEach(Consumer action) { delegate.forEach(action); } + } + + static class ThrowingSet extends ForwardingSet { + public ThrowingSet(Set delegate) { + super(delegate); + } + + @Override + public ThrowingIterator iterator() { + return new ThrowingIterator<>(super.iterator()); + } + } + + static abstract class ForwardingMap implements Map { + private final Map delegate; + + public ForwardingMap(Map delegate) { + this.delegate = delegate; + } + + @Override public int size() { return delegate.size(); } + @Override public boolean isEmpty() { return delegate.isEmpty(); } + @Override public boolean containsKey(Object o) { return delegate.containsKey(o); } + @Override public boolean containsValue(Object o) { return delegate.containsValue(o); } + @Override public V get(Object o) { return delegate.get(o); } + @Override public V put(K k, V v) { return delegate.put(k, v); } + @Override public V remove(Object o) { return delegate.remove(o); } + @Override public void putAll(Map map) { delegate.putAll(map); } + @Override public void clear() { delegate.clear(); } + @Override public Set keySet() { return delegate.keySet(); } + @Override public Collection values() { return delegate.values(); } + @Override public Set> entrySet() { return delegate.entrySet(); } + @Override public boolean equals(Object o) { return delegate.equals(o); } + @Override public int hashCode() { return delegate.hashCode(); } + @Override public V getOrDefault(Object key, V defaultValue) { return delegate.getOrDefault(key, defaultValue); } + @Override public void forEach(BiConsumer action) { delegate.forEach(action); } + @Override public void replaceAll(BiFunction function) { delegate.replaceAll(function); } + @Override public V putIfAbsent(K key, V value) { return delegate.putIfAbsent(key, value); } + @Override public boolean remove(Object key, Object value) { return delegate.remove(key, value); } + @Override public boolean replace(K key, V oldValue, V newValue) { return delegate.replace(key, oldValue, newValue); } + @Override public V replace(K key, V value) { return delegate.replace(key, value); } + @Override public V computeIfAbsent(K key, Function mappingFunction) { return delegate.computeIfAbsent(key, mappingFunction); } + @Override public V computeIfPresent(K key, BiFunction remappingFunction) { return delegate.computeIfPresent(key, remappingFunction); } + @Override public V compute(K key, BiFunction remappingFunction) { return delegate.compute(key, remappingFunction); } + @Override public V merge(K key, V value, BiFunction remappingFunction) { return delegate.merge(key, value, remappingFunction); } + } + + static class ThrowingMap extends ForwardingMap { + public ThrowingMap(Map delegate) { + super(delegate); + } + + @Override + public ThrowingSet> entrySet() { + return new ThrowingSet<>(super.entrySet()); + } + + @Override + public Set keySet() { + return new ThrowingSet(super.keySet()); + } + } + + static void assertThrowingIterator(Iterator iterator) { + try { + iterator.forEachRemaining((entry) -> {}); + Assert.fail(); + } catch (UnsupportedOperationException expected) { + } + } + + private static Map map() { + Map map = new HashMap<>(); + map.put("name", "Bill"); + map.put("age", 23); + return new ThrowingMap<>(map); + } + + @Test public void testUnwrapped() { + assertThrowingIterator(map().entrySet().iterator()); + assertThrowingIterator(map().keySet().iterator()); + } + + @Test public void test_unmodifiableMap_entrySet() { + assertThrowingIterator(Collections.unmodifiableMap(map()).entrySet().iterator()); + } + + @Test public void test_checkedMap_entrySet() { + assertThrowingIterator(Collections.checkedMap(map(), String.class, Object.class).entrySet().iterator()); + } + + @Test public void test_entrySet_checkedSet() { + Set> entrySet = map().entrySet(); + Class clazz = entrySet.iterator().next().getClass(); + assertThrowingIterator(Collections.checkedSet(entrySet, clazz).iterator()); + } +}