// Copyright 2014 The Bazel Authors. All rights reserved. // // 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.devtools.build.lib.collect; import static com.google.common.collect.ImmutableSet.toImmutableSet; import static java.util.stream.Collectors.toCollection; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; /** * Utilities for collection classes. */ public final class CollectionUtils { private CollectionUtils() {} /** * Given a collection of elements and an equivalence relation, returns a new * unordered collection of the disjoint subsets of those elements which are * equivalent under the specified relation. * *

Note: the Comparator needs only to implement the less-strict contract * of EquivalenceRelation (q.v.). (Hopefully this will one day be a * superinterface of Comparator.) * * @param elements the collection of elements to be partitioned. May * contain duplicates. * @param equivalenceRelation an equivalence relation over the elements. * @return a collection of sets of elements that are equivalent under the * specified relation. */ public static Collection> partitionWithComparator(Collection elements, Comparator equivalenceRelation) { // TODO(bazel-team): (2009) O(n*m) where n=|elements| and m=|eqClasses|; i.e., // quadratic. Use Tarjan's algorithm instead. List> eqClasses = new ArrayList<>(); for (T element : elements) { boolean found = false; for (Set eqClass : eqClasses) { if (equivalenceRelation.compare(eqClass.iterator().next(), element) == 0) { eqClass.add(element); found = true; break; } } if (!found) { Set eqClass = new HashSet<>(); eqClass.add(element); eqClasses.add(eqClass); } } return eqClasses; } /** * See partition(Collection, Comparator). */ public static Collection> partition(Collection elements, final EquivalenceRelation equivalenceRelation) { return partitionWithComparator(elements, (Comparator) equivalenceRelation::compare); } /** * Returns the set of all elements in the given collection that appear more than once. * @param input some collection. * @return the set of repeated elements. May return an empty set, but never null. */ public static Set duplicatedElementsOf(Iterable input) { Set duplicates = new HashSet<>(); Set elementSet = new HashSet<>(); for (T el : input) { if (!elementSet.add(el)) { duplicates.add(el); } } return duplicates; } /** * Returns an immutable set of all non-null parameters in the order in which they are specified. */ @SuppressWarnings("unchecked") public static ImmutableSet asSetWithoutNulls(T... elements) { return Arrays.stream(elements).filter(Objects::nonNull).collect(toImmutableSet()); } /** * Returns true if the given iterable can be verified to be immutable. * *

Note that if this method returns false, that does not mean that the iterable is mutable. */ public static boolean isImmutable(Iterable iterable) { return iterable instanceof ImmutableList || iterable instanceof ImmutableSet || iterable instanceof IterablesChain || iterable instanceof DedupingIterable || iterable instanceof NestedSet || iterable instanceof ImmutableIterable; } /** * Throws a runtime exception if the given iterable can not be verified to be immutable. */ public static void checkImmutable(Iterable iterable) { Preconditions.checkState(isImmutable(iterable), iterable.getClass()); } /** * Given an iterable, returns an immutable iterable with the same contents. */ public static Iterable makeImmutable(Iterable iterable) { return isImmutable(iterable) ? iterable : ImmutableList.copyOf(iterable); } /** * Converts a set of enum values to a bit field. Requires that the enum contains at most 32 * elements. */ public static > int toBits(Set values) { int result = 0; for (T value : values) { //

Note that when the 32. bit is set, the integer becomes negative (because that is the // sign bit). This does not affect the function of the bitwise operators, so it is fine. Preconditions.checkArgument(value.ordinal() < 32); result |= (1 << value.ordinal()); } return result; } /** * Converts a set of enum values to a bit field. Requires that the enum contains at most 32 * elements. */ @SuppressWarnings("unchecked") public static > int toBits(T... values) { return toBits(ImmutableSet.copyOf(values)); } /** * Converts a bit field to a set of enum values. Requires that the enum contains at most 32 * elements. */ public static > EnumSet fromBits(int value, Class clazz) { T[] elements = clazz.getEnumConstants(); Preconditions.checkArgument(elements.length <= 32); return Arrays.stream(elements) .filter(element -> (value & (1 << element.ordinal())) != 0) .collect(toCollection(() -> EnumSet.noneOf(clazz))); } /** * Returns whether an {@link Iterable} is a superset of another one. */ public static boolean containsAll(Iterable superset, Iterable subset) { return ImmutableSet.copyOf(superset).containsAll(ImmutableList.copyOf(subset)); } /** * Returns an ImmutableMap of ImmutableMaps created from the Map of Maps parameter. */ public static ImmutableMap> toImmutable( Map> map) { return ImmutableMap.copyOf(Maps.transformValues(map, ImmutableMap::copyOf)); } /** * Returns a copy of the Map of Maps parameter. */ public static Map> copyOf( Map> map) { return new HashMap<>(Maps.transformValues(map, HashMap::new)); } /** * A variant of {@link com.google.common.collect.Iterables.isEmpty} that avoids expanding nested * sets. */ public static boolean isEmpty(Iterable iterable) { return (iterable instanceof NestedSet) ? ((NestedSet) iterable).isEmpty() : Iterables.isEmpty(iterable); } }