CollectionUtil.java

/*
 * Portions of this software was developed by employees of the National Institute
 * of Standards and Technology (NIST), an agency of the Federal Government and is
 * being made available as a public service. Pursuant to title 17 United States
 * Code Section 105, works of NIST employees are not subject to copyright
 * protection in the United States. This software may be subject to foreign
 * copyright. Permission in the United States and in foreign countries, to the
 * extent that NIST may hold copyright, to use, copy, modify, create derivative
 * works, and distribute this software and its documentation without fee is hereby
 * granted on a non-exclusive basis, provided that this notice and disclaimer
 * of warranty appears in all copies.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
 */

package gov.nist.secauto.metaschema.core.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;

public final class CollectionUtil {

  private CollectionUtil() {
    // disable construction
  }

  /**
   * Get a {@link Stream} for the provided {@link Iterable}.
   *
   * @param <T>
   *          the type to iterate on
   * @param iterator
   *          the iterator
   * @return the stream
   */
  public static <T> Stream<T> toStream(@NonNull Iterator<T> iterator) {
    Iterable<T> iterable = toIterable(iterator);
    return StreamSupport.stream(iterable.spliterator(), false);
  }

  /**
   * Get an {@link Iterable} for the provided {@link Stream}.
   *
   * @param <T>
   *          the type to iterate on
   * @param stream
   *          the stream to iterate over
   * @return the resulting iterable instance
   */
  @NonNull
  public static <T> Iterable<T> toIterable(@NonNull Stream<T> stream) {
    return toIterable(ObjectUtils.notNull(stream.iterator()));
  }

  /**
   * Get an {@link Iterable} for the provided {@link Iterator}.
   *
   * @param <T>
   *          the type to iterate on
   * @param iterator
   *          the iterator
   * @return the resulting iterable instance
   */
  @NonNull
  public static <T> Iterable<T> toIterable(@NonNull Iterator<T> iterator) {
    return () -> iterator;
  }

  /**
   * Get a reverse {@link Iterable} for the provided {@link List}.
   *
   * @param <T>
   *          the type to iterate on
   * @param list
   *          the list of items to iterate over
   * @return the resulting iterable instance
   */
  @NonNull
  public static <T> Iterable<T> toDescendingIterable(@NonNull List<T> list) {
    return toIterable(descendingIterator(list));
  }

  /**
   * Convert the provided {@link Iterable} to a list of the same generic type.
   *
   * @param <T>
   *          the collection item's generic type
   * @param iterable
   *          the Iterable to convert to a list
   * @return the list
   */
  @NonNull
  public static <T> List<T> toList(Iterable<T> iterable) {
    return ObjectUtils.notNull(StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList()));
  }

  /**
   * Convert the provided {@link Iterator} to a list of the same generic type.
   *
   * @param <T>
   *          the collection item's generic type
   * @param iterator
   *          the Iterator to convert to a list
   * @return the list
   */
  @NonNull
  public static <T> List<T> toList(Iterator<T> iterator) {
    return ObjectUtils.notNull(
        StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false)
            .collect(Collectors.toList()));
  }

  /**
   * Get a reverse {@link Iterator} for the provided {@link List}.
   *
   * @param <T>
   *          the type to iterate on
   * @param list
   *          the list of items to iterate over
   * @return the resulting Iterator instance
   */
  @NonNull
  public static <T> Iterator<T> descendingIterator(@NonNull List<T> list) {
    Iterator<T> retval;
    if (list instanceof LinkedList) {
      retval = ((LinkedList<T>) list).descendingIterator();
    } else if (list instanceof ArrayList) {
      retval = IntStream.range(0, list.size())
          .map(i -> list.size() - 1 - i)
          .mapToObj(list::get).iterator();
    } else {
      throw new UnsupportedOperationException();
    }
    return ObjectUtils.notNull(retval);
  }

  @NonNull
  public static <T extends Collection<A>, A> T requireNonEmpty(@NonNull T collection) {
    if (collection.isEmpty()) {
      throw new IllegalStateException();
    }
    return collection;
  }

  @NonNull
  public static <T extends Collection<A>, A> T requireNonEmpty(@NonNull T collection, @NonNull String message) {
    if (collection.isEmpty()) {
      throw new IllegalStateException(message);
    }
    return collection;
  }

  /**
   * A wrapper of the {@link Collections#unmodifiableCollection(Collection)}
   * method that ensure a {@link NonNull} result is returned.
   *
   * @param <T>
   *          the collection's item type
   * @param collection
   *          the collection
   * @return a non-null unmodifiable instance of the provided collection
   */
  @SuppressWarnings("null")
  @NonNull
  public static <T> Collection<T> unmodifiableCollection(@NonNull Collection<T> collection) {
    return Collections.unmodifiableCollection(collection);
  }

  @SuppressWarnings("null")
  @NonNull
  public static <T> Set<T> singleton(@NonNull T value) {
    return Collections.singleton(value);
  }

  @SuppressWarnings("null")
  @NonNull
  public static <T> Set<T> emptySet() {
    return Collections.emptySet();
  }

  @SuppressWarnings("null")
  @NonNull
  public static <T> Set<T> unmodifiableSet(@NonNull Set<T> set) {
    return Collections.unmodifiableSet(set);
  }

  @NonNull
  public static <T> List<T> listOrEmpty(@Nullable List<T> list) {
    return list == null ? emptyList() : list;
  }

  @SafeVarargs
  @SuppressWarnings("null")
  @NonNull
  public static <T> List<T> listOrEmpty(@Nullable T... array) {
    return array == null || array.length == 0 ? emptyList() : Arrays.asList(array);
  }

  @SuppressWarnings("null")
  @NonNull
  public static <T> List<T> emptyList() {
    return Collections.emptyList();
  }

  @SuppressWarnings("null")
  @NonNull
  public static <T> List<T> unmodifiableList(@NonNull List<T> list) {
    return Collections.unmodifiableList(list);
  }

  @SuppressWarnings("null")
  @NonNull
  public static <T> List<T> singletonList(@NonNull T instance) {
    return Collections.singletonList(instance);
  }

  @SuppressWarnings("null")
  @NonNull
  public static <K, V> Map<K, V> emptyMap() {
    return Collections.emptyMap();
  }

  @SuppressWarnings("null")
  @NonNull
  public static <K, V> Map<K, V> singletonMap(@NonNull K key, @NonNull V value) {
    return Collections.singletonMap(key, value);
  }

  @SuppressWarnings("null")
  @NonNull
  public static <K, V> Map<K, V> unmodifiableMap(@NonNull Map<K, V> map) {
    return Collections.unmodifiableMap(map);
  }
}