All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sourceforge.pmd.util.CollectionUtil Maven / Gradle / Ivy

Go to download

PMD is an extensible multilanguage static code analyzer. It finds common programming flaws like unused variables, empty catch blocks, unnecessary object creation, and so forth. It's mainly concerned with Java and Apex, but supports 16 other languages. It comes with 400+ built-in rules. It can be extended with custom rules. It uses JavaCC and Antlr to parse source files into abstract syntax trees (AST) and runs rules against them to find violations. Rules can be written in Java or using a XPath query. Currently, PMD supports Java, JavaScript, Salesforce.com Apex and Visualforce, Kotlin, Swift, Modelica, PLSQL, Apache Velocity, JSP, WSDL, Maven POM, HTML, XML and XSL. Scala is supported, but there are currently no Scala rules available. Additionally, it includes CPD, the copy-paste-detector. CPD finds duplicated code in Coco, C/C++, C#, Dart, Fortran, Gherkin, Go, Groovy, HTML, Java, JavaScript, JSP, Julia, Kotlin, Lua, Matlab, Modelica, Objective-C, Perl, PHP, PLSQL, Python, Ruby, Salesforce.com Apex and Visualforce, Scala, Swift, T-SQL, Typescript, Apache Velocity, WSDL, XML and XSL.

There is a newer version: 7.5.0-metrics
Show newest version
/**
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
 */

package net.sourceforge.pmd.util;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyIterator;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collector.Characteristics;
import java.util.stream.Collectors;

import org.apache.commons.lang3.Validate;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.pcollections.ConsPStack;
import org.pcollections.HashTreePSet;
import org.pcollections.PMap;
import org.pcollections.PSequence;
import org.pcollections.PSet;

import net.sourceforge.pmd.lang.document.Chars;

/**
 * Generic collection-related utility functions for java.util types.
 *
 * @author Brian Remedios
 * @author Clément Fournier
 */
public final class CollectionUtil {

    private static final int UNKNOWN_SIZE = -1;

    private CollectionUtil() {
    }

    /**
     * Returns a list view that pretends it is the concatenation of
     * both lists. The returned view is unmodifiable. The implementation
     * is pretty stupid and not optimized for repeated concatenation,
     * but should be ok for smallish chains of random-access lists.
     *
     * @param head Head elements (to the left)
     * @param tail Tail elements (to the right)
     * @param   Type of elements in both lists
     *
     * @return A concatenated view
     */
    public static  List concatView(List head, List tail) {
        if (head.isEmpty()) {
            return makeUnmodifiableAndNonNull(tail);
        } else if (tail.isEmpty()) {
            return makeUnmodifiableAndNonNull(head);
        } else {
            return new ConsList<>(head, tail);
        }
    }


    /**
     * Returns the set union of the given collections.
     *
     * @param c1 First collection
     * @param c2 Second collection
     *
     * @return Union of both arguments
     */
    @SafeVarargs
    public static  Set union(Collection c1, Collection c2, Collection... rest) {
        Set union = new LinkedHashSet<>(c1);
        union.addAll(c2);
        for (Collection ts : rest) {
            union.addAll(ts);
        }
        return union;
    }

    /**
     * Returns the set intersection of the given collections.
     *
     * @param c1 First collection
     * @param c2 Second collection
     *
     * @return Intersection of both arguments
     */
    @SafeVarargs
    public static  Set intersect(Collection c1, Collection c2, Collection... rest) {
        Set union = new LinkedHashSet<>(c1);
        union.retainAll(c2);
        for (Collection ts : rest) {
            union.retainAll(ts);
        }
        return union;
    }


    /**
     * Returns the set difference of the first collection with the other
     * collections.
     *
     * @param c1 First collection
     * @param c2 Second collection
     *
     * @return Difference of arguments
     */
    @SafeVarargs
    public static  Set diff(Collection c1, Collection c2, Collection... rest) {
        Set union = new LinkedHashSet<>(c1);
        union.removeAll(c2);
        for (Collection ts : rest) {
            union.removeAll(ts);
        }
        return union;
    }

    /**
     * Returns a set containing the given elements. No guarantee is
     * made about mutability.
     *
     * @param first First element
     * @param rest  Following elements
     */
    @SafeVarargs
    public static  Set setOf(T first, T... rest) {
        return immutableSetOf(first, rest);
    }

    /**
     * Returns an unmodifiable set containing the given elements.
     *
     * @param first First element
     * @param rest  Following elements
     */
    @SafeVarargs
    public static  Set immutableSetOf(T first, T... rest) {
        if (rest.length == 0) {
            return Collections.singleton(first);
        }
        Set union = new LinkedHashSet<>();
        union.add(first);
        Collections.addAll(union, rest);
        return Collections.unmodifiableSet(union);
    }

    /**
     * Returns an unmodifiable set containing the given elements.
     *
     * @param first First element
     * @param rest  Following elements
     */
    @SafeVarargs
    public static > Set immutableEnumSet(T first, T... rest) {
        return Collections.unmodifiableSet(EnumSet.of(first, rest));
    }


    @SafeVarargs
    public static  List listOf(T first, T... rest) {
        if (rest.length == 0) {
            return ConsPStack.singleton(first);
        }
        List union = new ArrayList<>();
        union.add(first);
        union.addAll(asList(rest));
        return Collections.unmodifiableList(union);
    }

    public static  Map mapOf(K k0, V v0) {
        return Collections.singletonMap(k0, v0);
    }

    public static  Map mapOf(K k1, V v1, K k2, V v2) {
        Map map = new LinkedHashMap<>();
        map.put(k1, v1);
        map.put(k2, v2);
        return Collections.unmodifiableMap(map);
    }

    public static  Map buildMap(Consumer> effect) {
        Map map = new LinkedHashMap<>();
        effect.accept(map);
        return Collections.unmodifiableMap(map);
    }

    public static  Map buildMap(Map initialMap, Consumer> effect) {
        Map map = new LinkedHashMap<>(initialMap);
        effect.accept(map);
        return Collections.unmodifiableMap(map);
    }

    public static  List<@NonNull R> mapNotNull(Iterable from, Function f) {
        Iterator it = from.iterator();
        if (!it.hasNext()) {
            return Collections.emptyList();
        }
        List res = new ArrayList<>();
        while (it.hasNext()) {
            R r = f.apply(it.next());
            if (r != null) {
                res.add(r);
            }
        }
        return res;
    }

    /**
     * Produce a new map with the mappings of the first, and one additional
     * mapping. The returned map may be unmodifiable.
     */
    public static  Map plus(Map m, K k, V v) {
        AssertionUtil.requireParamNotNull("map", m);
        if (m instanceof PMap) {
            return ((PMap) m).plus(k, v);
        }
        if (m.isEmpty()) {
            return Collections.singletonMap(k, v);
        }
        Map newM = new HashMap<>(m);
        newM.put(k, v);
        return newM;
    }

    /**
     * Produce a new list with the elements of the first, and one additional
     * item. The returned list is immutable.
     */
    public static  List plus(List list, V v) {
        if (list instanceof PSequence) {
            return ((PSequence) list).plus(v);
        } else if (list.isEmpty()) {
            return ConsPStack.singleton(v);
        }
        return ConsPStack.from(list).plus(v);
    }

    /** Returns the empty list. */
    public static  List emptyList() {
        // We use this implementation so that it plays well with other
        // operations that expect immutable data.
        return ConsPStack.empty();
    }

    /**
     * Returns an unmodifiable set containing the set union of the collection,
     * and the new elements.
     */
    @SafeVarargs
    @SuppressWarnings("unchecked")
    public static  Set setUnion(Collection set, V first, V... newElements) {
        if (set instanceof PSet) {
            return ((PSet) set).plus(first).plusAll(asList(newElements));
        }
        Set newSet = new LinkedHashSet<>(set.size() + 1 + newElements.length);
        newSet.addAll(set);
        newSet.add(first);
        Collections.addAll(newSet, newElements);
        return Collections.unmodifiableSet(newSet);
    }

    /**
     * Returns the key that corresponds to the given value in the map,
     * or null if it is not contained in the map.
     *
     * @param m   Map
     * @param v   Value
     * @param  Type of keys
     * @param  Type of values
     *
     * @throws NullPointerException If the entry is found, but the key
     *                              is null
     * @throws NullPointerException If the map is null
     */
    public static <@NonNull K, V> @Nullable K getKeyOfValue(Map m, V v) {
        AssertionUtil.requireParamNotNull("map", m);
        for (Entry it : m.entrySet()) {
            if (it.getValue().equals(v)) {
                return Objects.requireNonNull(it.getKey(), "This method uses null as a sentinel value");
            }
        }
        return null;
    }


    /**
     * Returns a map associating each key in the first list to its
     * corresponding value in the second.
     *
     * @throws IllegalArgumentException If the list size are mismatched
     * @throws NullPointerException     If either of the parameter is null,
     *                                  or any of the keys or values are null
     */
    public static  Map zip(List from, List to) {
        AssertionUtil.requireParamNotNull("keys", from);
        AssertionUtil.requireParamNotNull("values", to);
        Validate.isTrue(from.size() == to.size(), "Mismatched list sizes %s to %s", from, to);

        if (from.isEmpty()) { //NOPMD: we really want to compare references here
            return emptyMap();
        }

        Map map = new HashMap<>(from.size());

        for (int i = 0; i < from.size(); i++) {
            K key = from.get(i);
            V val = to.get(i);

            Validate.notNull(key);
            Validate.notNull(val);

            map.put(key, val);
        }

        return map;
    }

    public static  Map associateWith(Collection keys, Function mapper) {
        AssertionUtil.requireParamNotNull("keys", keys);
        if (keys.isEmpty()) {
            return emptyMap();
        }

        return associateWithTo(new HashMap<>(keys.size()), keys, mapper);
    }

    public static  Map associateWithTo(Map collector, Collection keys, Function mapper) {
        AssertionUtil.requireParamNotNull("collector", collector);
        AssertionUtil.requireParamNotNull("keys", keys);
        AssertionUtil.requireParamNotNull("mapper", mapper);
        for (K key : keys) {
            collector.put(key, mapper.apply(key));
        }
        return collector;
    }


    public static  Map associateBy(Collection values, Function keyMapper) {
        AssertionUtil.requireParamNotNull("values", values);
        if (values.isEmpty()) {
            return emptyMap();
        }

        return associateByTo(new HashMap<>(values.size()), values, keyMapper);
    }


    public static  Map associateByTo(Map collector, Collection values, Function keyMapper) {
        AssertionUtil.requireParamNotNull("collector", collector);
        AssertionUtil.requireParamNotNull("values", values);
        AssertionUtil.requireParamNotNull("keyMapper", keyMapper);
        for (V v : values) {
            collector.put(keyMapper.apply(v), v);
        }
        return collector;
    }

    /**
     * Map each element of the given collection with the given function,
     * and accumulates it into an unmodifiable list.
     */
    public static  List map(Collection from, Function f) {
        if (from == null) {
            return emptyList();
        }
        return map(from.iterator(), from.size(), f);
    }

    /**
     * Map each element of the given iterable with the given function,
     * and accumulates it into an unmodifiable list.
     */
    public static  List map(Iterable from, Function f) {
        if (from == null) {
            return emptyList();
        }
        return map(from.iterator(), UNKNOWN_SIZE, f);
    }

    /**
     * Map each element of the given array with the given function,
     * and accumulates it into an unmodifiable list.
     */
    public static  List map(T[] from, Function f) {
        if (from == null) {
            return emptyList();
        }
        return map(asList(from), f);
    }

    /**
     * Map each element of the given iterator with the given function,
     * and accumulates it into an unmodifiable list.
     */
    public static  List map(Iterator from, Function f) {
        if (from == null) {
            return emptyList();
        }
        return map(from, UNKNOWN_SIZE, f);
    }

    private static  List map(Iterator from, int sizeHint, Function f) {
        if (!from.hasNext()) {
            return emptyList();
        } else if (sizeHint == 1) {
            return ConsPStack.singleton(f.apply(from.next()));
        }
        List res = sizeHint == UNKNOWN_SIZE ? new ArrayList<>() : new ArrayList<>(sizeHint);
        while (from.hasNext()) {
            res.add(f.apply(from.next()));
        }
        return Collections.unmodifiableList(res);
    }

    /**
     * Map each element of the given iterable with the given function,
     * and accumulates it into the collector.
     */
    public static  C map(Collector collector,
                                     Iterable from,
                                     Function f) {
        if (from == null) {
            return map(collector, emptyIterator(), f);
        }
        return map(collector, from.iterator(), f);
    }

    /**
     * Map each element of the given iterator with the given function,
     * and accumulates it into the collector.
     */
    // one more type param and we can write tupac
    public static  C map(Collector collector,
                                     Iterator from,
                                     Function f) {
        A a = collector.supplier().get();
        BiConsumer accumulator = collector.accumulator();
        from.forEachRemaining(t -> accumulator.accept(a, f.apply(t)));
        return finish(collector, a);
    }

    /**
     * A collector that returns a mutable list. This contrasts with
     * {@link Collectors#toList()}, which makes no guarantee about the
     * mutability of the list.
     *
     * @param  Type of accumulated values
     */
    public static  Collector> toMutableList() {
        return Collectors.toCollection(ArrayList::new);
    }


    /**
     * A collector that returns a mutable set. This contrasts with
     * {@link Collectors#toSet()}, which makes no guarantee about the
     * mutability of the set. The set preserves insertion order.
     *
     * @param  Type of accumulated values
     */
    public static  Collector> toMutableSet() {
        return Collectors.toCollection(LinkedHashSet::new);
    }

    /**
     * A collector that returns an unmodifiable list. This contrasts with
     * {@link Collectors#toList()}, which makes no guarantee about the
     * mutability of the list. {@code Collectors::toUnmodifiableList} was
     * only added in JDK 9.
     *
     * @param  Type of accumulated values
     */
    public static  Collector> toUnmodifiableList() {
        return Collectors.collectingAndThen(toMutableList(), Collections::unmodifiableList);
    }

    /**
     * A collector that returns an unmodifiable set. This contrasts with
     * {@link Collectors#toSet()}, which makes no guarantee about the
     * mutability of the set. {@code Collectors::toUnmodifiableSet} was
     * only added in JDK 9. The set preserves insertion order.
     *
     * @param  Type of accumulated values
     */
    public static  Collector> toUnmodifiableSet() {
        return Collectors.collectingAndThen(toMutableSet(), Collections::unmodifiableSet);
    }

    /**
     * A collectors that accumulates into a persistent set.
     *
     * @param  Type of accumulated values
     */
    public static  Collector> toPersistentSet() {
        class Holder {

            PSet set = HashTreePSet.empty();
        }

        return Collector.of(
            Holder::new,
            (h, t) -> h.set = h.set.plus(t),
            (left, right) -> {
                left.set = left.set.plusAll(right.set);
                return left;
            },
            a -> a.set
        );
    }

    /**
     * Finish the accumulated value of the collector.
     */
    public static  C finish(Collector collector, A acc) {
        if (collector.characteristics().contains(Characteristics.IDENTITY_FINISH)) {
            return (C) acc;
        } else {
            return collector.finisher().apply(acc);
        }
    }

    public static  List drop(List list, int n) {
        AssertionUtil.requireNonNegative("n", n);

        return list.size() <= n ? emptyList()
                                : list.subList(n, list.size());
    }

    public static  List take(List list, int n) {
        AssertionUtil.requireNonNegative("n", n);
        return list.size() <= n ? list
                                : list.subList(0, n);
    }


    public static  List listOfNotNull(T t) {
        return t == null ? emptyList() : ConsPStack.singleton(t);
    }

    /**
     * Returns true if any element of the iterable matches the predicate. Return
     * false if the list is null or empty.
     */
    public static  boolean any(@Nullable Iterable list, Predicate predicate) {
        return list != null && IteratorUtil.anyMatch(list.iterator(), predicate);
    }

    /**
     * Returns true if all elements of the iterable match the predicate. Return
     * true if the list is null or empty.
     */
    public static  boolean all(@Nullable Iterable list, Predicate predicate) {
        return list == null || IteratorUtil.allMatch(list.iterator(), predicate);
    }

    /**
     * Returns true if no element of the iterable matches the predicate. Return
     * true if the list is null or empty.
     */
    public static  boolean none(@Nullable Iterable list, Predicate predicate) {
        return list == null || IteratorUtil.noneMatch(list.iterator(), predicate);
    }

    /**
     * If the set has a single element, returns it, otherwise returns null.
     * Obviously the set should not contain null elements.
     */
    public static <@NonNull T> @Nullable T asSingle(Set set) {
        if (set.size() == 1) {
            return set.iterator().next();
        } else {
            return null;
        }
    }

    /**
     * Returns an unmodifiable copy of the list. This is to be preferred
     * to {@link Collections#unmodifiableList(List)} if you don't trust
     * the source of the list, because no one holds a reference to the buffer
     * except the returned unmodifiable list.
     *
     * @param list A list
     * @param   Type of items
     */
    public static  List defensiveUnmodifiableCopy(List list) {
        if (list instanceof PSequence) {
            return (List) list; // is already immutable
        }
        if (list.isEmpty()) {
            return ConsPStack.empty();
        } else if (list.size() == 1) {
            return ConsPStack.singleton(list.get(0));
        }
        return ConsPStack.from(list);
    }

    public static  Set defensiveUnmodifiableCopyToSet(Collection list) {
        if (list.isEmpty()) {
            return emptySet();
        }
        return Collections.unmodifiableSet(new LinkedHashSet<>(list));
    }

    /**
     * Like {@link String#join(CharSequence, Iterable)}, except it appends
     * on a preexisting {@link StringBuilder}. The result value is that StringBuilder.
     */
    public static  StringBuilder joinOn(StringBuilder sb,
                                           Iterable iterable,
                                           BiConsumer appendItem,
                                           String delimiter) {
        boolean first = true;
        for (T t : iterable) {
            if (first) {
                first = false;
            } else {
                sb.append(delimiter);
            }
            appendItem.accept(sb, t);
        }
        return sb;
    }

    public static @NonNull StringBuilder joinCharsIntoStringBuilder(List lines, String delimiter) {
        return joinOn(
            new StringBuilder(),
            lines,
            (buf, line) -> line.appendChars(buf),
            delimiter
        );
    }


    /**
     * Merge the second map into the first. If some keys are in common,
     * merge them using the merge function, like {@link Map#merge(Object, Object, BiFunction)}.
     */
    public static  void mergeMaps(Map result, Map other, BinaryOperator mergeFun) {
        for (K otherKey : other.keySet()) {
            V otherInfo = other.get(otherKey); // non-null
            result.merge(otherKey, otherInfo, mergeFun);
        }
    }

    /**
     * Union of two PSets, which avoids creating a new pset if possible.
     */
    public static  PSet union(PSet as, PSet bs) {
        if (as.isEmpty()) {
            return bs;
        } else if (bs.isEmpty()) {
            return as;
        }
        return as.plusAll(bs);
    }

    public static @NonNull  List makeUnmodifiableAndNonNull(@Nullable List list) {
        if (list instanceof PSequence) {
            return (List) list;
        }
        return list == null || list.isEmpty() ? emptyList()
                                              : Collections.unmodifiableList(list);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy