diff options
Diffstat (limited to 'android/guava/src/com/google/common/collect/CollectCollectors.java')
-rw-r--r-- | android/guava/src/com/google/common/collect/CollectCollectors.java | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/android/guava/src/com/google/common/collect/CollectCollectors.java b/android/guava/src/com/google/common/collect/CollectCollectors.java new file mode 100644 index 000000000..b9ae7cba0 --- /dev/null +++ b/android/guava/src/com/google/common/collect/CollectCollectors.java @@ -0,0 +1,473 @@ +/* + * Copyright (C) 2016 The Guava Authors + * + * 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.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Collections.singletonMap; +import static java.util.stream.Collectors.collectingAndThen; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.Preconditions; +import java.util.Collection; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.TreeMap; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.ToIntFunction; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.annotation.CheckForNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** Collectors utilities for {@code common.collect} internals. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +@SuppressWarnings({"AndroidJdkLibsChecker", "Java7ApiChecker"}) +@IgnoreJRERequirement // used only from APIs with Java 8 types in them +// (not used publicly by guava-android as of this writing, but we include it in the jar as a test) +final class CollectCollectors { + + private static final Collector<Object, ?, ImmutableList<Object>> TO_IMMUTABLE_LIST = + Collector.of( + ImmutableList::builder, + ImmutableList.Builder::add, + ImmutableList.Builder::combine, + ImmutableList.Builder::build); + + private static final Collector<Object, ?, ImmutableSet<Object>> TO_IMMUTABLE_SET = + Collector.of( + ImmutableSet::builder, + ImmutableSet.Builder::add, + ImmutableSet.Builder::combine, + ImmutableSet.Builder::build); + + @GwtIncompatible + private static final Collector<Range<Comparable<?>>, ?, ImmutableRangeSet<Comparable<?>>> + TO_IMMUTABLE_RANGE_SET = + Collector.of( + ImmutableRangeSet::builder, + ImmutableRangeSet.Builder::add, + ImmutableRangeSet.Builder::combine, + ImmutableRangeSet.Builder::build); + + // Lists + + @SuppressWarnings({"rawtypes", "unchecked"}) + static <E> Collector<E, ?, ImmutableList<E>> toImmutableList() { + return (Collector) TO_IMMUTABLE_LIST; + } + + // Sets + + @SuppressWarnings({"rawtypes", "unchecked"}) + static <E> Collector<E, ?, ImmutableSet<E>> toImmutableSet() { + return (Collector) TO_IMMUTABLE_SET; + } + + static <E> Collector<E, ?, ImmutableSortedSet<E>> toImmutableSortedSet( + Comparator<? super E> comparator) { + checkNotNull(comparator); + return Collector.of( + () -> new ImmutableSortedSet.Builder<E>(comparator), + ImmutableSortedSet.Builder::add, + ImmutableSortedSet.Builder::combine, + ImmutableSortedSet.Builder::build); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + static <E extends Enum<E>> Collector<E, ?, ImmutableSet<E>> toImmutableEnumSet() { + return (Collector) EnumSetAccumulator.TO_IMMUTABLE_ENUM_SET; + } + + private static <E extends Enum<E>> + Collector<E, EnumSetAccumulator<E>, ImmutableSet<E>> toImmutableEnumSetGeneric() { + return Collector.of( + EnumSetAccumulator::new, + EnumSetAccumulator::add, + EnumSetAccumulator::combine, + EnumSetAccumulator::toImmutableSet, + Collector.Characteristics.UNORDERED); + } + + @IgnoreJRERequirement // see enclosing class (whose annotation Animal Sniffer ignores here...) + private static final class EnumSetAccumulator<E extends Enum<E>> { + @SuppressWarnings({"rawtypes", "unchecked"}) + static final Collector<Enum<?>, ?, ImmutableSet<? extends Enum<?>>> TO_IMMUTABLE_ENUM_SET = + (Collector) toImmutableEnumSetGeneric(); + + @CheckForNull private EnumSet<E> set; + + void add(E e) { + if (set == null) { + set = EnumSet.of(e); + } else { + set.add(e); + } + } + + EnumSetAccumulator<E> combine(EnumSetAccumulator<E> other) { + if (this.set == null) { + return other; + } else if (other.set == null) { + return this; + } else { + this.set.addAll(other.set); + return this; + } + } + + ImmutableSet<E> toImmutableSet() { + if (set == null) { + return ImmutableSet.of(); + } + ImmutableSet<E> ret = ImmutableEnumSet.asImmutable(set); + set = null; // subsequent manual manipulation of the accumulator mustn't affect ret + return ret; + } + } + + @GwtIncompatible + @SuppressWarnings({"rawtypes", "unchecked"}) + static <E extends Comparable<? super E>> + Collector<Range<E>, ?, ImmutableRangeSet<E>> toImmutableRangeSet() { + return (Collector) TO_IMMUTABLE_RANGE_SET; + } + + // Multisets + + static <T extends @Nullable Object, E> Collector<T, ?, ImmutableMultiset<E>> toImmutableMultiset( + Function<? super T, ? extends E> elementFunction, ToIntFunction<? super T> countFunction) { + checkNotNull(elementFunction); + checkNotNull(countFunction); + return Collector.of( + LinkedHashMultiset::create, + (multiset, t) -> + multiset.add(checkNotNull(elementFunction.apply(t)), countFunction.applyAsInt(t)), + (multiset1, multiset2) -> { + multiset1.addAll(multiset2); + return multiset1; + }, + (Multiset<E> multiset) -> ImmutableMultiset.copyFromEntries(multiset.entrySet())); + } + + static <T extends @Nullable Object, E extends @Nullable Object, M extends Multiset<E>> + Collector<T, ?, M> toMultiset( + Function<? super T, E> elementFunction, + ToIntFunction<? super T> countFunction, + Supplier<M> multisetSupplier) { + checkNotNull(elementFunction); + checkNotNull(countFunction); + checkNotNull(multisetSupplier); + return Collector.of( + multisetSupplier, + (ms, t) -> ms.add(elementFunction.apply(t), countFunction.applyAsInt(t)), + (ms1, ms2) -> { + ms1.addAll(ms2); + return ms1; + }); + } + + // Maps + + static <T extends @Nullable Object, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap( + Function<? super T, ? extends K> keyFunction, + Function<? super T, ? extends V> valueFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + return Collector.of( + ImmutableMap.Builder<K, V>::new, + (builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)), + ImmutableMap.Builder::combine, + ImmutableMap.Builder::buildOrThrow); + } + + static <T extends @Nullable Object, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap( + Function<? super T, ? extends K> keyFunction, + Function<? super T, ? extends V> valueFunction, + BinaryOperator<V> mergeFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + checkNotNull(mergeFunction); + return collectingAndThen( + Collectors.toMap(keyFunction, valueFunction, mergeFunction, LinkedHashMap::new), + ImmutableMap::copyOf); + } + + static <T extends @Nullable Object, K, V> + Collector<T, ?, ImmutableSortedMap<K, V>> toImmutableSortedMap( + Comparator<? super K> comparator, + Function<? super T, ? extends K> keyFunction, + Function<? super T, ? extends V> valueFunction) { + checkNotNull(comparator); + checkNotNull(keyFunction); + checkNotNull(valueFunction); + /* + * We will always fail if there are duplicate keys, and the keys are always sorted by + * the Comparator, so the entries can come in an arbitrary order -- so we report UNORDERED. + */ + return Collector.of( + () -> new ImmutableSortedMap.Builder<K, V>(comparator), + (builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)), + ImmutableSortedMap.Builder::combine, + ImmutableSortedMap.Builder::buildOrThrow, + Collector.Characteristics.UNORDERED); + } + + static <T extends @Nullable Object, K, V> + Collector<T, ?, ImmutableSortedMap<K, V>> toImmutableSortedMap( + Comparator<? super K> comparator, + Function<? super T, ? extends K> keyFunction, + Function<? super T, ? extends V> valueFunction, + BinaryOperator<V> mergeFunction) { + checkNotNull(comparator); + checkNotNull(keyFunction); + checkNotNull(valueFunction); + checkNotNull(mergeFunction); + return collectingAndThen( + Collectors.toMap( + keyFunction, valueFunction, mergeFunction, () -> new TreeMap<K, V>(comparator)), + ImmutableSortedMap::copyOfSorted); + } + + static <T extends @Nullable Object, K, V> Collector<T, ?, ImmutableBiMap<K, V>> toImmutableBiMap( + Function<? super T, ? extends K> keyFunction, + Function<? super T, ? extends V> valueFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + return Collector.of( + ImmutableBiMap.Builder<K, V>::new, + (builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)), + ImmutableBiMap.Builder::combine, + ImmutableBiMap.Builder::buildOrThrow, + new Collector.Characteristics[0]); + } + + @J2ktIncompatible + static <T extends @Nullable Object, K extends Enum<K>, V> + Collector<T, ?, ImmutableMap<K, V>> toImmutableEnumMap( + Function<? super T, ? extends K> keyFunction, + Function<? super T, ? extends V> valueFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + return Collector.of( + () -> + new EnumMapAccumulator<K, V>( + (v1, v2) -> { + throw new IllegalArgumentException("Multiple values for key: " + v1 + ", " + v2); + }), + (accum, t) -> { + /* + * We assign these to variables before calling checkNotNull to work around a bug in our + * nullness checker. + */ + K key = keyFunction.apply(t); + V newValue = valueFunction.apply(t); + accum.put( + checkNotNull(key, "Null key for input %s", t), + checkNotNull(newValue, "Null value for input %s", t)); + }, + EnumMapAccumulator::combine, + EnumMapAccumulator::toImmutableMap, + Collector.Characteristics.UNORDERED); + } + + @J2ktIncompatible + static <T extends @Nullable Object, K extends Enum<K>, V> + Collector<T, ?, ImmutableMap<K, V>> toImmutableEnumMap( + Function<? super T, ? extends K> keyFunction, + Function<? super T, ? extends V> valueFunction, + BinaryOperator<V> mergeFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + checkNotNull(mergeFunction); + // not UNORDERED because we don't know if mergeFunction is commutative + return Collector.of( + () -> new EnumMapAccumulator<K, V>(mergeFunction), + (accum, t) -> { + /* + * We assign these to variables before calling checkNotNull to work around a bug in our + * nullness checker. + */ + K key = keyFunction.apply(t); + V newValue = valueFunction.apply(t); + accum.put( + checkNotNull(key, "Null key for input %s", t), + checkNotNull(newValue, "Null value for input %s", t)); + }, + EnumMapAccumulator::combine, + EnumMapAccumulator::toImmutableMap); + } + + @J2ktIncompatible + @IgnoreJRERequirement // see enclosing class (whose annotation Animal Sniffer ignores here...) + private static class EnumMapAccumulator<K extends Enum<K>, V> { + private final BinaryOperator<V> mergeFunction; + @CheckForNull private EnumMap<K, V> map = null; + + EnumMapAccumulator(BinaryOperator<V> mergeFunction) { + this.mergeFunction = mergeFunction; + } + + void put(K key, V value) { + if (map == null) { + map = new EnumMap<>(singletonMap(key, value)); + } else { + map.merge(key, value, mergeFunction); + } + } + + EnumMapAccumulator<K, V> combine(EnumMapAccumulator<K, V> other) { + if (this.map == null) { + return other; + } else if (other.map == null) { + return this; + } else { + other.map.forEach(this::put); + return this; + } + } + + ImmutableMap<K, V> toImmutableMap() { + return (map == null) ? ImmutableMap.<K, V>of() : ImmutableEnumMap.asImmutable(map); + } + } + + @GwtIncompatible + static <T extends @Nullable Object, K extends Comparable<? super K>, V> + Collector<T, ?, ImmutableRangeMap<K, V>> toImmutableRangeMap( + Function<? super T, Range<K>> keyFunction, + Function<? super T, ? extends V> valueFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + return Collector.of( + ImmutableRangeMap::<K, V>builder, + (builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)), + ImmutableRangeMap.Builder::combine, + ImmutableRangeMap.Builder::build); + } + + // Multimaps + + static <T extends @Nullable Object, K, V> + Collector<T, ?, ImmutableListMultimap<K, V>> toImmutableListMultimap( + Function<? super T, ? extends K> keyFunction, + Function<? super T, ? extends V> valueFunction) { + checkNotNull(keyFunction, "keyFunction"); + checkNotNull(valueFunction, "valueFunction"); + return Collector.of( + ImmutableListMultimap::<K, V>builder, + (builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)), + ImmutableListMultimap.Builder::combine, + ImmutableListMultimap.Builder::build); + } + + static <T extends @Nullable Object, K, V> + Collector<T, ?, ImmutableListMultimap<K, V>> flatteningToImmutableListMultimap( + Function<? super T, ? extends K> keyFunction, + Function<? super T, ? extends Stream<? extends V>> valuesFunction) { + checkNotNull(keyFunction); + checkNotNull(valuesFunction); + return collectingAndThen( + flatteningToMultimap( + input -> checkNotNull(keyFunction.apply(input)), + input -> valuesFunction.apply(input).peek(Preconditions::checkNotNull), + MultimapBuilder.linkedHashKeys().arrayListValues()::<K, V>build), + ImmutableListMultimap::copyOf); + } + + static <T extends @Nullable Object, K, V> + Collector<T, ?, ImmutableSetMultimap<K, V>> toImmutableSetMultimap( + Function<? super T, ? extends K> keyFunction, + Function<? super T, ? extends V> valueFunction) { + checkNotNull(keyFunction, "keyFunction"); + checkNotNull(valueFunction, "valueFunction"); + return Collector.of( + ImmutableSetMultimap::<K, V>builder, + (builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)), + ImmutableSetMultimap.Builder::combine, + ImmutableSetMultimap.Builder::build); + } + + static <T extends @Nullable Object, K, V> + Collector<T, ?, ImmutableSetMultimap<K, V>> flatteningToImmutableSetMultimap( + Function<? super T, ? extends K> keyFunction, + Function<? super T, ? extends Stream<? extends V>> valuesFunction) { + checkNotNull(keyFunction); + checkNotNull(valuesFunction); + return collectingAndThen( + flatteningToMultimap( + input -> checkNotNull(keyFunction.apply(input)), + input -> valuesFunction.apply(input).peek(Preconditions::checkNotNull), + MultimapBuilder.linkedHashKeys().linkedHashSetValues()::<K, V>build), + ImmutableSetMultimap::copyOf); + } + + static < + T extends @Nullable Object, + K extends @Nullable Object, + V extends @Nullable Object, + M extends Multimap<K, V>> + Collector<T, ?, M> toMultimap( + Function<? super T, ? extends K> keyFunction, + Function<? super T, ? extends V> valueFunction, + Supplier<M> multimapSupplier) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + checkNotNull(multimapSupplier); + return Collector.of( + multimapSupplier, + (multimap, input) -> multimap.put(keyFunction.apply(input), valueFunction.apply(input)), + (multimap1, multimap2) -> { + multimap1.putAll(multimap2); + return multimap1; + }); + } + + static < + T extends @Nullable Object, + K extends @Nullable Object, + V extends @Nullable Object, + M extends Multimap<K, V>> + Collector<T, ?, M> flatteningToMultimap( + Function<? super T, ? extends K> keyFunction, + Function<? super T, ? extends Stream<? extends V>> valueFunction, + Supplier<M> multimapSupplier) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + checkNotNull(multimapSupplier); + return Collector.of( + multimapSupplier, + (multimap, input) -> { + K key = keyFunction.apply(input); + Collection<V> valuesForKey = multimap.get(key); + valueFunction.apply(input).forEachOrdered(valuesForKey::add); + }, + (multimap1, multimap2) -> { + multimap1.putAll(multimap2); + return multimap1; + }); + } + + private CollectCollectors() {} +} |