1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.core.util;
7   
8   import java.util.ArrayList;
9   import java.util.Arrays;
10  import java.util.Collection;
11  import java.util.Collections;
12  import java.util.Iterator;
13  import java.util.LinkedList;
14  import java.util.List;
15  import java.util.Map;
16  import java.util.Set;
17  import java.util.Spliterator;
18  import java.util.Spliterators;
19  import java.util.stream.Collectors;
20  import java.util.stream.IntStream;
21  import java.util.stream.Stream;
22  import java.util.stream.StreamSupport;
23  
24  import edu.umd.cs.findbugs.annotations.NonNull;
25  import edu.umd.cs.findbugs.annotations.Nullable;
26  
27  /**
28   * Provides a collection of utilities for handling Java collections and
29   * iterators.
30   */
31  // FIXME: Rename to CollectionUtils
32  @SuppressWarnings("PMD.CouplingBetweenObjects")
33  public final class CollectionUtil {
34    /**
35     * Get a {@link Stream} for the provided {@link Iterable}.
36     *
37     * @param <T>
38     *          the type to iterate on
39     * @param iterator
40     *          the iterator
41     * @return the stream
42     */
43    public static <T> Stream<T> toStream(@NonNull Iterator<T> iterator) {
44      Iterable<T> iterable = toIterable(iterator);
45      return StreamSupport.stream(iterable.spliterator(), false);
46    }
47  
48    /**
49     * Get an {@link Iterable} for the provided {@link Stream}.
50     *
51     * @param <T>
52     *          the type to iterate on
53     * @param stream
54     *          the stream to iterate over
55     * @return the resulting iterable instance
56     */
57    @NonNull
58    public static <T> Iterable<T> toIterable(@NonNull Stream<T> stream) {
59      return toIterable(ObjectUtils.notNull(stream.iterator()));
60    }
61  
62    /**
63     * Get an {@link Iterable} for the provided {@link Iterator}.
64     *
65     * @param <T>
66     *          the type to iterate on
67     * @param iterator
68     *          the iterator
69     * @return the resulting iterable instance
70     */
71    @NonNull
72    public static <T> Iterable<T> toIterable(@NonNull Iterator<T> iterator) {
73      return () -> iterator;
74    }
75  
76    /**
77     * Get a reverse {@link Iterable} for the provided {@link List}.
78     *
79     * @param <T>
80     *          the type to iterate on
81     * @param list
82     *          the list of items to iterate over
83     * @return the resulting iterable instance
84     */
85    @NonNull
86    public static <T> Iterable<T> toDescendingIterable(@NonNull List<T> list) {
87      return toIterable(descendingIterator(list));
88    }
89  
90    /**
91     * Convert the provided {@link Iterable} to a list of the same generic type.
92     *
93     * @param <T>
94     *          the collection item's generic type
95     * @param iterable
96     *          the Iterable to convert to a list
97     * @return the list
98     */
99    @NonNull
100   public static <T> List<T> toList(Iterable<T> iterable) {
101     return ObjectUtils.notNull(StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList()));
102   }
103 
104   /**
105    * Convert the provided {@link Iterator} to a list of the same generic type.
106    *
107    * @param <T>
108    *          the collection item's generic type
109    * @param iterator
110    *          the Iterator to convert to a list
111    * @return the list
112    */
113   @NonNull
114   public static <T> List<T> toList(Iterator<T> iterator) {
115     return ObjectUtils.notNull(
116         StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false)
117             .collect(Collectors.toList()));
118   }
119 
120   /**
121    * Get a reverse {@link Iterator} for the provided {@link List}.
122    *
123    * @param <T>
124    *          the type to iterate on
125    * @param list
126    *          the list of items to iterate over
127    * @return the resulting Iterator instance
128    */
129   @NonNull
130   public static <T> Iterator<T> descendingIterator(@NonNull List<T> list) {
131     Iterator<T> retval;
132     if (list instanceof LinkedList) {
133       retval = ((LinkedList<T>) list).descendingIterator();
134     } else if (list instanceof ArrayList) {
135       retval = IntStream.range(0, list.size())
136           .map(i -> list.size() - 1 - i)
137           .mapToObj(list::get).iterator();
138     } else {
139       throw new UnsupportedOperationException();
140     }
141     return ObjectUtils.notNull(retval);
142   }
143 
144   /**
145    * Require that the provided collection contains at least a single item.
146    *
147    * @param <T>
148    *          the Java type of the collection
149    * @param <U>
150    *          the Java type of the collection's items
151    * @param collection
152    *          the collection to test
153    * @return the provided collection
154    * @throws IllegalStateException
155    *           if the collection is empty
156    */
157   @NonNull
158   public static <T extends Collection<U>, U> T requireNonEmpty(@NonNull T collection) {
159     if (collection.isEmpty()) {
160       throw new IllegalStateException();
161     }
162     return collection;
163   }
164 
165   /**
166    * Require that the provided collection contains at least a single item.
167    *
168    * @param <T>
169    *          the Java type of the collection
170    * @param <U>
171    *          the Java type of the collection's items
172    * @param collection
173    *          the collection to test
174    * @param message
175    *          the exception message to use if the collection is empty
176    * @return the provided collection
177    * @throws IllegalStateException
178    *           if the collection is empty
179    */
180   @NonNull
181   public static <T extends Collection<U>, U> T requireNonEmpty(@NonNull T collection, @NonNull String message) {
182     if (collection.isEmpty()) {
183       throw new IllegalStateException(message);
184     }
185     return collection;
186   }
187 
188   /**
189    * An implementation of {@link Collections#unmodifiableCollection(Collection)}
190    * that respects non-nullness.
191    *
192    * @param <T>
193    *          the collection's item type
194    * @param collection
195    *          the collection
196    * @return an unmodifiable view of the collection
197    */
198   @SuppressWarnings("null")
199   @NonNull
200   public static <T> Collection<T> unmodifiableCollection(@NonNull Collection<T> collection) {
201     return Collections.unmodifiableCollection(collection);
202   }
203 
204   /**
205    * An implementation of {@link Collections#singleton(Object)} that respects
206    * non-nullness.
207    *
208    * @param <T>
209    *          the Java type of the set items
210    * @param instance
211    *          the singleton item to use
212    * @return an unmodifiable set containing the singleton item
213    */
214   @SuppressWarnings("null")
215   @NonNull
216   public static <T> Set<T> singleton(@NonNull T instance) {
217     return Collections.singleton(instance);
218   }
219 
220   /**
221    * An implementation of {@link Collections#emptySet()} that respects
222    * non-nullness.
223    *
224    * @param <T>
225    *          the Java type of the set items
226    * @return an unmodifiable empty set
227    */
228   @SuppressWarnings("null")
229   @NonNull
230   public static <T> Set<T> emptySet() {
231     return Collections.emptySet();
232   }
233 
234   /**
235    * An implementation of {@link Collections#unmodifiableSet(Set)} that respects
236    * non-nullness.
237    *
238    * @param <T>
239    *          the Java type of the set items
240    * @param set
241    *          the set to prevent modification of
242    * @return an unmodifiable view of the set
243    */
244   @SuppressWarnings("null")
245   @NonNull
246   public static <T> Set<T> unmodifiableSet(@NonNull Set<T> set) {
247     return Collections.unmodifiableSet(set);
248   }
249 
250   /**
251    * Provides an unmodifiable list containing the provided list.
252    * <p>
253    * If the provided list is {@code null}, an empty list will be provided.
254    *
255    * @param <T>
256    *          the Java type of the list items
257    * @param list
258    *          the list, which may be {@code null}
259    * @return an unmodifiable list containing the items
260    */
261   @NonNull
262   public static <T> List<T> listOrEmpty(@Nullable List<T> list) {
263     return list == null ? emptyList() : unmodifiableList(list);
264   }
265 
266   /**
267    * Generates a new unmodifiable list containing the provided items.
268    * <p>
269    * If the provided array is {@code null}, an empty list will be provided.
270    *
271    * @param <T>
272    *          the Java type of the list items
273    * @param array
274    *          the array of items to use to populate the list, which may be
275    *          {@code null}
276    * @return an unmodifiable list containing the items
277    */
278   @SafeVarargs
279   @SuppressWarnings("null")
280   @NonNull
281   public static <T> List<T> listOrEmpty(@Nullable T... array) {
282     return array == null || array.length == 0 ? emptyList() : unmodifiableList(Arrays.asList(array));
283   }
284 
285   /**
286    * An implementation of {@link Collections#emptyList()} that respects
287    * non-nullness.
288    *
289    * @param <T>
290    *          the Java type of the list items
291    * @return an unmodifiable empty list
292    */
293   @SuppressWarnings("null")
294   @NonNull
295   public static <T> List<T> emptyList() {
296     return Collections.emptyList();
297   }
298 
299   /**
300    * An implementation of {@link Collections#unmodifiableList(List)} that respects
301    * non-nullness.
302    *
303    * @param <T>
304    *          the Java type of the list items
305    * @param list
306    *          the list to prevent modification of
307    * @return an unmodifiable view of the list
308    */
309   @SuppressWarnings("null")
310   @NonNull
311   public static <T> List<T> unmodifiableList(@NonNull List<T> list) {
312     return Collections.unmodifiableList(list);
313   }
314 
315   /**
316    * An implementation of {@link Collections#singletonList(Object)} that respects
317    * non-nullness.
318    *
319    * @param <T>
320    *          the Java type of the list items
321    * @param instance
322    *          the singleton item to use
323    * @return an unmodifiable list containing the singleton item
324    */
325   @SuppressWarnings("null")
326   @NonNull
327   public static <T> List<T> singletonList(@NonNull T instance) {
328     return Collections.singletonList(instance);
329   }
330 
331   /**
332    * An implementation of {@link Collections#emptyMap()} that respects
333    * non-nullness.
334    *
335    * @param <K>
336    *          the Java type of the map's keys
337    * @param <V>
338    *          the Java type of the map's values
339    * @return an unmodifiable empty map
340    */
341   @SuppressWarnings("null")
342   @NonNull
343   public static <K, V> Map<K, V> emptyMap() {
344     return Collections.emptyMap();
345   }
346 
347   /**
348    * An implementation of {@link Collections#singletonMap(Object, Object)} that
349    * respects non-nullness.
350    *
351    * @param <K>
352    *          the Java type of the map's keys
353    * @param <V>
354    *          the Java type of the map's values
355    * @param key
356    *          the singleton key
357    * @param value
358    *          the singleton value
359    * @return an unmodifiable map containing the singleton entry
360    */
361   @SuppressWarnings("null")
362   @NonNull
363   public static <K, V> Map<K, V> singletonMap(@NonNull K key, @NonNull V value) {
364     return Collections.singletonMap(key, value);
365   }
366 
367   /**
368    * An implementation of {@link Collections#unmodifiableMap(Map)} that respects
369    * non-nullness.
370    *
371    * @param map
372    *          the map to prevent modification of
373    * @param <K>
374    *          the Java type of the map's keys
375    * @param <V>
376    *          the Java type of the map's values
377    * @return an unmodifiable view of the map
378    */
379   @SuppressWarnings("null")
380   @NonNull
381   public static <K, V> Map<K, V> unmodifiableMap(@NonNull Map<K, V> map) {
382     return Collections.unmodifiableMap(map);
383   }
384 
385   private CollectionUtil() {
386     // disable construction
387   }
388 }