001/* 002 * SPDX-FileCopyrightText: none 003 * SPDX-License-Identifier: CC0-1.0 004 */ 005 006package dev.metaschema.core.util; 007 008import java.util.ArrayList; 009import java.util.Arrays; 010import java.util.Collection; 011import java.util.Collections; 012import java.util.Iterator; 013import java.util.LinkedList; 014import java.util.List; 015import java.util.Map; 016import java.util.Set; 017import java.util.Spliterator; 018import java.util.Spliterators; 019import java.util.stream.Collectors; 020import java.util.stream.IntStream; 021import java.util.stream.Stream; 022import java.util.stream.StreamSupport; 023 024import edu.umd.cs.findbugs.annotations.NonNull; 025import edu.umd.cs.findbugs.annotations.Nullable; 026 027/** 028 * Provides a collection of utilities for handling Java collections and 029 * iterators. 030 */ 031// FIXME: Rename to CollectionUtils 032@SuppressWarnings("PMD.CouplingBetweenObjects") 033public final class CollectionUtil { 034 /** 035 * Get a {@link Stream} for the provided {@link Iterable}. 036 * 037 * @param <T> 038 * the type to iterate on 039 * @param iterator 040 * the iterator 041 * @return the stream 042 */ 043 public static <T> Stream<T> toStream(@NonNull Iterator<T> iterator) { 044 Iterable<T> iterable = toIterable(iterator); 045 return StreamSupport.stream(iterable.spliterator(), false); 046 } 047 048 /** 049 * Get an {@link Iterable} for the provided {@link Stream}. 050 * 051 * @param <T> 052 * the type to iterate on 053 * @param stream 054 * the stream to iterate over 055 * @return the resulting iterable instance 056 */ 057 @NonNull 058 public static <T> Iterable<T> toIterable(@NonNull Stream<T> stream) { 059 return toIterable(ObjectUtils.notNull(stream.iterator())); 060 } 061 062 /** 063 * Get an {@link Iterable} for the provided {@link Iterator}. 064 * 065 * @param <T> 066 * the type to iterate on 067 * @param iterator 068 * the iterator 069 * @return the resulting iterable instance 070 */ 071 @NonNull 072 public static <T> Iterable<T> toIterable(@NonNull Iterator<T> iterator) { 073 return () -> iterator; 074 } 075 076 /** 077 * Get a reverse {@link Iterable} for the provided {@link List}. 078 * 079 * @param <T> 080 * the type to iterate on 081 * @param list 082 * the list of items to iterate over 083 * @return the resulting iterable instance 084 */ 085 @NonNull 086 public static <T> Iterable<T> toDescendingIterable(@NonNull List<T> list) { 087 return toIterable(descendingIterator(list)); 088 } 089 090 /** 091 * Convert the provided {@link Iterable} to a list of the same generic type. 092 * 093 * @param <T> 094 * the collection item's generic type 095 * @param iterable 096 * the Iterable to convert to a list 097 * @return the list 098 */ 099 @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}