001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.internal; 014 015import static org.apache.juneau.internal.StringUtils.*; 016import static org.apache.juneau.internal.ObjectUtils.*; 017 018import java.lang.reflect.*; 019import java.util.*; 020 021import org.apache.juneau.*; 022import org.apache.juneau.parser.*; 023 024/** 025 * Utility methods for collections. 026 */ 027public final class CollectionUtils { 028 029 /** 030 * Add a value to a list if the value is not null. 031 * 032 * @param l The list to add to. 033 * @param o The element to add. 034 * @return The same list. 035 */ 036 public static <T> List<T> addIfNotNull(List<T> l, T o) { 037 if (o != null) 038 l.add(o); 039 return l; 040 } 041 042 /** 043 * Adds the contents of one list to the other in reverse order. 044 * 045 * <p> 046 * i.e. add values from 2nd list from end-to-start order to the end of the 1st list. 047 * 048 * @param list The list to append to. 049 * @param append Contains the values to append to the list. 050 * @return The same list. 051 */ 052 @SuppressWarnings({ "rawtypes", "unchecked" }) 053 public static List<?> addReverse(List list, List append) { 054 for (ListIterator i = append.listIterator(append.size()); i.hasPrevious();) 055 list.add(i.previous()); 056 return list; 057 } 058 059 /** 060 * Adds the contents of the array to the list in reverse order. 061 * 062 * <p> 063 * i.e. add values from the array from end-to-start order to the end of the list. 064 * 065 * @param list The list to append to. 066 * @param append Contains the values to append to the list. 067 * @return The same list. 068 */ 069 @SuppressWarnings({ "rawtypes", "unchecked" }) 070 public static List<?> addReverse(List list, Object[] append) { 071 for (int i = append.length - 1; i >= 0; i--) 072 list.add(append[i]); 073 return list; 074 } 075 076 /** 077 * Returns an iterable over the specified collection. 078 * 079 * @param c The collection to iterate over. 080 * @param reverse If <jk>true</jk>, iterate in reverse order. 081 * @return An iterable over the collection. 082 */ 083 public static <T> Iterable<T> iterable(final Collection<T> c, boolean reverse) { 084 if (reverse) 085 return new ReverseIterable<>(c instanceof List ? (List<T>)c : new ArrayList<>(c)); 086 return c; 087 } 088 089 /** 090 * Returns an iterable over the specified list. 091 * 092 * @param c The collection to iterate over. 093 * @param reverse If <jk>true</jk>, iterate in reverse order. 094 * @return An iterable over the collection. 095 */ 096 public static <T> Iterable<T> iterable(final List<T> c, boolean reverse) { 097 if (reverse) 098 return new ReverseIterable<>(c); 099 return c; 100 } 101 102 /** 103 * Returns a reverse iterable over the specified list. 104 * 105 * @param c The collection to iterate over. 106 * @return An iterable over the collection. 107 */ 108 public static <T> Iterable<T> reverseIterable(final List<T> c) { 109 return iterable(c, true); 110 } 111 112 /** 113 * Returns an iterable over the specified array. 114 * 115 * @param c The collection to iterate over. 116 * @param reverse If <jk>true</jk>, iterate in reverse order. 117 * @return An iterable over the collection. 118 */ 119 public static <T> Iterable<T> iterable(T[] c, boolean reverse) { 120 if (reverse) 121 return new ReverseIterable<>(Arrays.asList(c)); 122 return Arrays.asList(c); 123 } 124 125 /** 126 * Returns a reverse iterable over the specified array. 127 * 128 * @param c The collection to iterate over. 129 * @return An iterable over the collection. 130 */ 131 public static <T> Iterable<T> reverseIterable(T[] c) { 132 return iterable(c, true); 133 } 134 135 /** 136 * Returns an iterable over the specified enumeration. 137 * 138 * @param e The collection to iterate over. 139 * @return An iterable over the enumeration. 140 */ 141 public static <E> Iterable<E> iterable(final Enumeration<E> e) { 142 if (e == null) 143 return null; 144 return new Iterable<E>() { 145 @Override 146 public Iterator<E> iterator() { 147 return new Iterator<E>() { 148 @Override 149 public boolean hasNext() { 150 return e.hasMoreElements(); 151 } 152 @Override 153 public E next() { 154 return e.nextElement(); 155 } 156 @Override 157 public void remove() { 158 throw new UnsupportedOperationException(); 159 } 160 }; 161 } 162 }; 163 } 164 165 /** 166 * Creates an unmodifiable list from the specified list. 167 * 168 * @param l The collection to copy from. 169 * @return An unmodifiable view of the list, or a {@link Collections#emptyList()} 170 * if the list was empty or <jk>null</jk>. 171 */ 172 public static <T> List<T> unmodifiableList(List<T> l) { 173 if (l == null || l.isEmpty()) 174 return Collections.emptyList(); 175 return Collections.unmodifiableList(l); 176 } 177 178 /** 179 * Creates an unmodifiable list from the specified array. 180 * 181 * @param l The collection to copy from. 182 * @return An unmodifiable view of the list, or a {@link Collections#emptyList()} 183 * if the list was empty or <jk>null</jk>. 184 */ 185 public static <T> List<T> unmodifiableList(T[] l) { 186 if (l == null || l.length == 0) 187 return Collections.emptyList(); 188 return Collections.unmodifiableList(Arrays.asList(l)); 189 } 190 191 /** 192 * Creates an immutable list from the specified collection. 193 * 194 * @param l The collection to copy from. 195 * @return An unmodifiable {@link ArrayList} copy of the collection, or a {@link Collections#emptyList()} 196 * if the collection was empty or <jk>null</jk>. 197 */ 198 public static <T> List<T> immutableList(Collection<T> l) { 199 if (l == null || l.isEmpty()) 200 return Collections.emptyList(); 201 return Collections.unmodifiableList(new ArrayList<>(l)); 202 } 203 204 /** 205 * Creates an immutable list from the specified array. 206 * 207 * @param l The array to copy from. 208 * @return An unmodifiable {@link ArrayList} copy of the collection, or a {@link Collections#emptyList()} 209 * if the collection was empty or <jk>null</jk>. 210 */ 211 public static <T> List<T> immutableList(T[] l) { 212 if (l == null || l.length == 0) 213 return Collections.emptyList(); 214 return Collections.unmodifiableList(new ArrayList<>(Arrays.asList(l))); 215 } 216 217 /** 218 * Creates an immutable map from the specified map. 219 * 220 * @param m The map to copy from. 221 * @return An unmodifiable {@link LinkedHashMap} copy of the collection, or a {@link Collections#emptyMap()} 222 * if the collection was empty or <jk>null</jk>. 223 */ 224 public static <K,V> Map<K,V> immutableMap(Map<K,V> m) { 225 if (m == null || m.isEmpty()) 226 return Collections.emptyMap(); 227 return Collections.unmodifiableMap(new LinkedHashMap<>(m)); 228 } 229 230 /** 231 * Creates an unmodifiable map from the specified map. 232 * 233 * @param m The map to copy from. 234 * @return An unmodifiable view of the collection, or a {@link Collections#emptyMap()} 235 * if the collection was empty or <jk>null</jk>. 236 */ 237 public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> m) { 238 if (m == null || m.isEmpty()) 239 return Collections.emptyMap(); 240 return Collections.unmodifiableMap(m); 241 } 242 243 /** 244 * Adds a set of values to an existing list. 245 * 246 * @param appendTo 247 * The list to append to. 248 * <br>If <jk>null</jk>, a new {@link ArrayList} will be created. 249 * @param values The values to add. 250 * @param type The data type of the elements. 251 * @param args The generic type arguments of the data type. 252 * @return The converted value, or <jk>null</jk> if the input was null. 253 */ 254 public static <T> List<T> addToList(List<T> appendTo, Object[] values, Class<T> type, Type...args) { 255 if (values == null) 256 return appendTo; 257 try { 258 List<T> l = appendTo; 259 if (appendTo == null) 260 l = new ArrayList<>(); 261 for (Object o : values) { 262 if (o != null) { 263 if (isObjectList(o, false)) { 264 for (Object o2 : new ObjectList(o.toString())) 265 l.add(toType(o2, type, args)); 266 } else if (o instanceof Collection) { 267 for (Object o2 : (Collection<?>)o) 268 l.add(toType(o2, type, args)); 269 } else if (o.getClass().isArray()) { 270 for (int i = 0; i < Array.getLength(o); i++) 271 l.add(toType(Array.get(o, i), type, args)); 272 } else { 273 l.add(toType(o, type, args)); 274 } 275 } 276 } 277 return l.isEmpty() ? null : l; 278 } catch (ParseException e) { 279 throw new RuntimeException(e); 280 } 281 } 282 283 /** 284 * Adds a set of values to an existing map. 285 * 286 * @param appendTo 287 * The map to append to. 288 * <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created. 289 * @param values The values to add. 290 * @param keyType The data type of the keys. 291 * @param valueType The data type of the values. 292 * @param valueTypeArgs The generic type arguments of the data type of the values. 293 * @return The converted value, or <jk>null</jk> if the input was null. 294 */ 295 @SuppressWarnings("unchecked") 296 public static <K,V> Map<K,V> addToMap(Map<K,V> appendTo, Object[] values, Class<K> keyType, Class<V> valueType, Type...valueTypeArgs) { 297 if (values == null) 298 return appendTo; 299 try { 300 Map<K,V> m = appendTo; 301 if (m == null) 302 m = new LinkedHashMap<>(); 303 for (Object o : values) { 304 if (o != null) { 305 if (isObjectMap(o, false)) { 306 for (Map.Entry<String,Object> e : new ObjectMap(o.toString()).entrySet()) 307 m.put(toType(e.getKey(), keyType), toType(e.getValue(), valueType, valueTypeArgs)); 308 } else if (o instanceof Map) { 309 for (Map.Entry<Object,Object> e : ((Map<Object,Object>)o).entrySet()) 310 m.put(toType(e.getKey(), keyType), toType(e.getValue(), valueType, valueTypeArgs)); 311 } else { 312 throw new FormattedRuntimeException("Invalid object type {0} passed to addToMap()", o.getClass().getName()); 313 } 314 } 315 } 316 return m.isEmpty() ? null : m; 317 } catch (ParseException e) { 318 throw new RuntimeException(e); 319 } 320 } 321 322 /** 323 * Creates a new list from the specified collection. 324 * 325 * @param val The value to copy from. 326 * @return A new {@link ArrayList}, or <jk>null</jk> if the input was null. 327 */ 328 public static <T> List<T> newList(Collection<T> val) { 329 if (val == null) 330 return null; 331 return new ArrayList<>(val); 332 } 333 334 /** 335 * Copies the specified values into an existing list. 336 * 337 * @param l 338 * The list to add to. 339 * <br>If <jk>null</jk>, a new {@link ArrayList} will be created. 340 * @param val The values to add. 341 * @return The list with values copied into it. 342 */ 343 public static <T> List<T> addToList(List<T> l, Collection<T> val) { 344 if (val != null) { 345 if (l == null) 346 l = new ArrayList<>(val); 347 else 348 l.addAll(val); 349 } 350 return l; 351 } 352 353 /** 354 * Creates a new map from the specified map. 355 * 356 * @param val The value to copy from. 357 * @return A new {@link LinkedHashMap}, or <jk>null</jk> if the input was null. 358 */ 359 public static <K,V> Map<K,V> newMap(Map<K,V> val) { 360 if (val == null) 361 return null; 362 return new LinkedHashMap<>(val); 363 } 364 365 /** 366 * Copies the specified values into an existing map. 367 * 368 * @param m 369 * The map to add to. 370 * <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created. 371 * @param val The values to add. 372 * @return The list with values copied into it. 373 */ 374 public static <K,V> Map<K,V> addToMap(Map<K,V> m, Map<K,V> val) { 375 if (val != null) { 376 if (m == null) 377 m = new LinkedHashMap<>(val); 378 else 379 m.putAll(val); 380 } 381 return m; 382 } 383 384 /** 385 * Adds a single entry into an existing map. 386 * 387 * @param m 388 * The map to add to. 389 * <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created. 390 * @param key The entry key. 391 * @param value The entry value. 392 * @return The list with values copied into it. 393 */ 394 public static <K,V> Map<K,V> addToMap(Map<K,V> m, K key, V value) { 395 if (m == null) 396 m = new LinkedHashMap<>(); 397 m.put(key, value); 398 return m; 399 } 400 401 /** 402 * Creates a new map from the specified map. 403 * 404 * @param val The value to copy from. 405 * @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering. 406 * @return A new {@link LinkedHashMap}, or <jk>null</jk> if the input was null. 407 */ 408 public static <K,V> Map<K,V> newSortedMap(Map<K,V> val, Comparator<K> comparator) { 409 if (val == null) 410 return null; 411 Map<K,V> m = new TreeMap<>(comparator); 412 m.putAll(val); 413 return m; 414 } 415 416 /** 417 * Creates a case-insensitive ordered set out of the specified string values. 418 * 419 * @param values The values to populate the set with. 420 * @return A new ordered set. 421 */ 422 public static Set<String> newSortedCaseInsensitiveSet(String...values) { 423 Set<String> s = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER) { 424 private static final long serialVersionUID = 1L; 425 @Override 426 public boolean contains(Object v) { 427 return v == null ? false : super.contains(v); 428 } 429 }; 430 for (String v : values) 431 if (v != null) 432 s.add(v); 433 return s; 434 } 435 436 /** 437 * Creates a case-insensitive ordered set out of the specified string values. 438 * 439 * @param values 440 * A comma-delimited list of the values to populate the set with. 441 * @return A new ordered set. 442 */ 443 public static Set<String> newSortedCaseInsensitiveSet(String values) { 444 return newSortedCaseInsensitiveSet(StringUtils.split(StringUtils.emptyIfNull(values))); 445 } 446 447 /** 448 * Same as {@link #newSortedCaseInsensitiveSet(String)} but makes the set unmodifiable. 449 * 450 * @param values 451 * A comma-delimited list of the values to populate the set with. 452 * @return A new ordered set. 453 */ 454 public static Set<String> newUnmodifiableSortedCaseInsensitiveSet(String values) { 455 return Collections.unmodifiableSet(newSortedCaseInsensitiveSet(StringUtils.split(StringUtils.emptyIfNull(values)))); 456 } 457 458 /** 459 * Copies the specified values into an existing map. 460 * 461 * @param m 462 * The map to add to. 463 * <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created. 464 * @param val The values to add. 465 * @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering. 466 * @return The list with values copied into it. 467 */ 468 public static <K,V> Map<K,V> addToSortedMap(Map<K,V> m, Map<K,V> val, Comparator<K> comparator) { 469 if (val != null) { 470 if (m == null) { 471 m = new TreeMap<>(comparator); 472 m.putAll(val); 473 } else { 474 m.putAll(val); 475 } 476 } 477 return m; 478 } 479 480 /** 481 * Adds a single entry into an existing map. 482 * 483 * @param m 484 * The map to add to. 485 * <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created. 486 * @param key The entry key. 487 * @param value The entry value. 488 * @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering. 489 * @return The list with values copied into it. 490 */ 491 public static <K,V> Map<K,V> addToSortedMap(Map<K,V> m, K key, V value, Comparator<K> comparator) { 492 if (m == null) 493 m = new TreeMap<>(comparator); 494 m.put(key, value); 495 return m; 496 } 497 498 /** 499 * Converts the specified arguments into an unmodifiable {@link HashSet}. 500 * 501 * @param values The entries to populate the hashset with. 502 * @return A new {@link HashSet} populated with the specified arguments. 503 */ 504 @SafeVarargs 505 public static <T> Set<T> newUnmodifiableHashSet(T...values) { 506 return Collections.unmodifiableSet(newHashSet(values)); 507 } 508 509 /** 510 * Converts the specified arguments into a modifiable {@link HashSet}. 511 * 512 * @param values The entries to populate the hashset with. 513 * @return A new {@link HashSet} populated with the specified arguments. 514 */ 515 @SafeVarargs 516 public static <T> Set<T> newHashSet(T...values) { 517 return new HashSet<>(Arrays.asList(values)); 518 } 519 520 /** 521 * Converts the specified arguments into an unmodifiable {@link LinkedHashSet}. 522 * 523 * @param values The entries to populate the hashset with. 524 * @return A new {@link LinkedHashSet} populated with the specified arguments. 525 */ 526 @SafeVarargs 527 public static <T> Set<T> newUnmodifiableLinkedHashSet(T...values) { 528 return Collections.unmodifiableSet(newLinkedHashSet(values)); 529 } 530 531 /** 532 * Converts the specified arguments into a modifiable {@link LinkedHashSet}. 533 * 534 * @param values The entries to populate the hashset with. 535 * @return A new {@link LinkedHashSet} populated with the specified arguments. 536 */ 537 @SafeVarargs 538 public static <T> Set<T> newLinkedHashSet(T...values) { 539 return new LinkedHashSet<>(Arrays.asList(values)); 540 } 541 542 /** 543 * Simple passthrough to {@link Collections#emptySet()} 544 * 545 * @return A new unmodifiable empty set. 546 */ 547 public static <T> Set<T> emptySet() { 548 return Collections.emptySet(); 549 } 550 551 /** 552 * Simple passthrough to {@link Collections#emptyList()} 553 * 554 * @return A new unmodifiable empty list. 555 */ 556 public static <T> List<T> emptyList() { 557 return Collections.emptyList(); 558 } 559 560 /** 561 * Simple passthrough to {@link Collections#emptyMap()} 562 * 563 * @return A new unmodifiable empty set. 564 */ 565 public static <K,V> Map<K,V> emptyMap() { 566 return Collections.emptyMap(); 567 } 568}