Skip to content

Commit

Permalink
Add Java 10 Collectors APIs (#9860)
Browse files Browse the repository at this point in the history
Fixes #9547
  • Loading branch information
niloc132 authored Dec 23, 2023
1 parent f439c5e commit c5d74f1
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 0 deletions.
35 changes: 35 additions & 0 deletions user/super/com/google/gwt/emul/java/util/stream/Collectors.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.DoubleSummaryStatistics;
import java.util.HashMap;
Expand All @@ -27,6 +28,7 @@
import java.util.List;
import java.util.LongSummaryStatistics;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
Expand Down Expand Up @@ -339,6 +341,11 @@ public static <T> Collector<T,?,List<T>> toList() {
return toCollection(ArrayList::new);
}

public static <T> Collector<T, ?, List<T>> toUnmodifiableList() {
Collector<T, ?, List<T>> mapping = mapping(Objects::requireNonNull, toList());
return collectingAndThen(mapping, Collections::unmodifiableList);
}

public static <T, K, U> Collector<T, ?, Map<K, U>> toMap(
final Function<? super T, ? extends K> keyMapper,
final Function<? super T, ? extends U> valueMapper) {
Expand All @@ -357,6 +364,29 @@ public static <T> Collector<T,?,List<T>> toList() {
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}

public static <T, K, U> Collector<T, ?, Map<K, U>> toUnmodifiableMap(
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
return collectingAndThen(
toMap(disallowNulls(keyMapper), disallowNulls(valueMapper)),
Collections::unmodifiableMap
);
}

public static <T, K, U> Collector<T, ?, Map<K, U>> toUnmodifiableMap(
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction) {
return collectingAndThen(
toMap(disallowNulls(keyMapper), disallowNulls(valueMapper), mergeFunction),
Collections::unmodifiableMap
);
}

private static <T, R> Function<T, R> disallowNulls(Function<T, R> func) {
return func.andThen(Objects::requireNonNull);
}

public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(
final Function<? super T, ? extends K> keyMapper,
final Function<? super T, ? extends U> valueMapper,
Expand Down Expand Up @@ -389,6 +419,11 @@ public static <T> Collector<T,?,Set<T>> toSet() {
);
}

public static <T> Collector<T, ?, Set<T>> toUnmodifiableSet() {
Collector<T, ?, Set<T>> mapping = mapping(Objects::requireNonNull, toSet());
return collectingAndThen(mapping, Collections::unmodifiableSet);
}

private static <T, D, A> D streamAndCollect(Collector<? super T, A, D> downstream, List<T> list) {
A a = downstream.supplier().get();
for (T t : list) {
Expand Down
2 changes: 2 additions & 0 deletions user/test/com/google/gwt/emultest/EmulJava10Suite.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@
import com.google.gwt.emultest.java10.util.OptionalIntTest;
import com.google.gwt.emultest.java10.util.OptionalLongTest;
import com.google.gwt.emultest.java10.util.OptionalTest;
import com.google.gwt.emultest.java10.util.stream.CollectorsTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

/** Test JRE emulations. */
@RunWith(Suite.class)
@SuiteClasses({
CollectorsTest.class,
OptionalDoubleTest.class,
OptionalIntTest.class,
OptionalLongTest.class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* Copyright 2023 Google Inc.
*
* Licensed 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 of
* 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 com.google.gwt.emultest.java10.util.stream;

import static com.google.gwt.emultest.java8.util.stream.CollectorsTest.applyItems;
import static java.util.stream.Collectors.toUnmodifiableList;
import static java.util.stream.Collectors.toUnmodifiableMap;
import static java.util.stream.Collectors.toUnmodifiableSet;

import com.google.gwt.emultest.java.util.EmulTestBase;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;

/**
* Tests for java.util.stream.Collectors Java 10 API emulation.
*/
public class CollectorsTest extends EmulTestBase {
private static <T> boolean unmodifiableCollection(Collection<T> c, T existingSample, T newSample) {
try {
c.remove(existingSample);
return false;
} catch (UnsupportedOperationException ignore) {
// expected
}
try {
c.add(newSample);
return false;
} catch (UnsupportedOperationException ignore) {
// expected
}
Iterator<T> itr = c.iterator();
itr.next();
try {
itr.remove();
return false;
} catch (UnsupportedOperationException e) {
// expected
}
return true;
}

public void testToUnmodifiableList() {
applyItems(List.of("a", "b"), toUnmodifiableList(), "a", "b", (expected, actual) -> {
if (!expected.equals(actual)) {
return false;
}

if (!unmodifiableCollection(actual, "a", "z")) {
return false;
}

return true;
});

// verify nulls fail
try {
Stream.of("a").map(ignore -> null).collect(toUnmodifiableList());
fail("Expected NPE");
} catch (NullPointerException ignore) {
// expected
}
}

public void testToUnmodifiableMap() {
// verify simple cases copy all values and results are unmodifiable
applyItems(Map.of("a", 0, "b", 1), toUnmodifiableMap(Function.identity(),
k -> k.charAt(0) - 'a'), "a", "b", (expected, actual) -> {
if (!expected.equals(actual)) {
return false;
}

if (!unmodifiableMap(actual, "a", 0, "z", 100)) {
return false;
}

return true;
});

// verify merge works with only one key (but this is just passing through to the toMap func
// anyway...)
applyItems(Map.of("a", 2), toUnmodifiableMap(Function.identity(), ignore -> 1, Integer::sum),
"a", "a");

// verify nulls blow up for both keys and values
try {
Stream.of("a").collect(toUnmodifiableMap(obj -> null, Function.identity()));
fail("Expected NPE");
} catch (NullPointerException ignore) {
// expected
}
try {
Stream.of("a").collect(toUnmodifiableMap(Function.identity(), obj -> null));
fail("Expected NPE");
} catch (Exception ignore) {
// expected
}
}

private <K, V> boolean unmodifiableMap(Map<K, V> a, K existingKey, V existingValue, K newKey,
V newValue) {
if (!unmodifiableCollection(a.keySet(), existingKey, newKey)) {
return false;
}
if (!unmodifiableCollection(a.values(), existingValue, newValue)) {
return false;
}

try {
a.put(newKey, newValue);
return false;
} catch (Exception ignore) {
// expected
}
try {
a.remove(existingKey);
return false;
} catch (Exception ignore) {
// expected
}

return true;
}

public void testToUnmodifiableSet() {
applyItems(Set.of("a", "b"), toUnmodifiableSet(), "a", "b", (expected, actual) -> {
if (!expected.equals(actual)) {
return false;
}
if (!unmodifiableCollection(actual, "a", "z")) {
return false;
}
return true;
});

// verify nulls fail
try {
Stream.of("a").map(ignore -> null).collect(toUnmodifiableSet());
fail("Expected NPE");
} catch (NullPointerException ignore) {
// expected
}
}
}

0 comments on commit c5d74f1

Please sign in to comment.