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 an iterable over the specified array. 104 * 105 * @param c The collection to iterate over. 106 * @param reverse If <jk>true</jk>, iterate in reverse order. 107 * @return An iterable over the collection. 108 */ 109 public static <T> Iterable<T> iterable(T[] c, boolean reverse) { 110 if (reverse) 111 return new ReverseIterable<>(Arrays.asList(c)); 112 return Arrays.asList(c); 113 } 114 115 /** 116 * Returns an iterable over the specified enumeration. 117 * 118 * @param e The collection to iterate over. 119 * @return An iterable over the enumeration. 120 */ 121 public static <E> Iterable<E> iterable(final Enumeration<E> e) { 122 if (e == null) 123 return null; 124 return new Iterable<E>() { 125 @Override 126 public Iterator<E> iterator() { 127 return new Iterator<E>() { 128 @Override 129 public boolean hasNext() { 130 return e.hasMoreElements(); 131 } 132 @Override 133 public E next() { 134 return e.nextElement(); 135 } 136 @Override 137 public void remove() { 138 throw new UnsupportedOperationException(); 139 } 140 }; 141 } 142 }; 143 } 144 145 /** 146 * Creates an unmodifiable list from the specified list. 147 * 148 * @param l The collection to copy from. 149 * @return An unmodifiable view of the list, or a {@link Collections#emptyList()} 150 * if the list was empty or <jk>null</jk>. 151 */ 152 public static <T> List<T> unmodifiableList(List<T> l) { 153 if (l == null || l.isEmpty()) 154 return Collections.emptyList(); 155 return Collections.unmodifiableList(l); 156 } 157 158 /** 159 * Creates an unmodifiable list from the specified array. 160 * 161 * @param l The collection to copy from. 162 * @return An unmodifiable view of the list, or a {@link Collections#emptyList()} 163 * if the list was empty or <jk>null</jk>. 164 */ 165 public static <T> List<T> unmodifiableList(T[] l) { 166 if (l == null || l.length == 0) 167 return Collections.emptyList(); 168 return Collections.unmodifiableList(Arrays.asList(l)); 169 } 170 171 /** 172 * Creates an immutable list from the specified collection. 173 * 174 * @param l The collection to copy from. 175 * @return An unmodifiable {@link ArrayList} copy of the collection, or a {@link Collections#emptyList()} 176 * if the collection was empty or <jk>null</jk>. 177 */ 178 public static <T> List<T> immutableList(Collection<T> l) { 179 if (l == null || l.isEmpty()) 180 return Collections.emptyList(); 181 return Collections.unmodifiableList(new ArrayList<>(l)); 182 } 183 184 /** 185 * Creates an immutable list from the specified array. 186 * 187 * @param l The array to copy from. 188 * @return An unmodifiable {@link ArrayList} copy of the collection, or a {@link Collections#emptyList()} 189 * if the collection was empty or <jk>null</jk>. 190 */ 191 public static <T> List<T> immutableList(T[] l) { 192 if (l == null || l.length == 0) 193 return Collections.emptyList(); 194 return Collections.unmodifiableList(new ArrayList<>(Arrays.asList(l))); 195 } 196 197 /** 198 * Creates an immutable map from the specified map. 199 * 200 * @param m The map to copy from. 201 * @return An unmodifiable {@link LinkedHashMap} copy of the collection, or a {@link Collections#emptyMap()} 202 * if the collection was empty or <jk>null</jk>. 203 */ 204 public static <K,V> Map<K,V> immutableMap(Map<K,V> m) { 205 if (m == null || m.isEmpty()) 206 return Collections.emptyMap(); 207 return Collections.unmodifiableMap(new LinkedHashMap<>(m)); 208 } 209 210 /** 211 * Creates an unmodifiable map from the specified map. 212 * 213 * @param m The map to copy from. 214 * @return An unmodifiable view of the collection, or a {@link Collections#emptyMap()} 215 * if the collection was empty or <jk>null</jk>. 216 */ 217 public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> m) { 218 if (m == null || m.isEmpty()) 219 return Collections.emptyMap(); 220 return Collections.unmodifiableMap(m); 221 } 222 223 /** 224 * Adds a set of values to an existing list. 225 * 226 * @param appendTo 227 * The list to append to. 228 * <br>If <jk>null</jk>, a new {@link ArrayList} will be created. 229 * @param values The values to add. 230 * @param type The data type of the elements. 231 * @param args The generic type arguments of the data type. 232 * @return The converted value, or <jk>null</jk> if the input was null. 233 */ 234 public static <T> List<T> addToList(List<T> appendTo, Object[] values, Class<T> type, Type...args) { 235 if (values == null) 236 return appendTo; 237 try { 238 List<T> l = appendTo; 239 if (appendTo == null) 240 l = new ArrayList<>(); 241 for (Object o : values) { 242 if (o != null) { 243 if (isObjectList(o, false)) { 244 for (Object o2 : new ObjectList(o.toString())) 245 l.add(toType(o2, type, args)); 246 } else if (o instanceof Collection) { 247 for (Object o2 : (Collection<?>)o) 248 l.add(toType(o2, type, args)); 249 } else if (o.getClass().isArray()) { 250 for (int i = 0; i < Array.getLength(o); i++) 251 l.add(toType(Array.get(o, i), type, args)); 252 } else { 253 l.add(toType(o, type, args)); 254 } 255 } 256 } 257 return l.isEmpty() ? null : l; 258 } catch (ParseException e) { 259 throw new RuntimeException(e); 260 } 261 } 262 263 /** 264 * Adds a set of values to an existing map. 265 * 266 * @param appendTo 267 * The map to append to. 268 * <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created. 269 * @param values The values to add. 270 * @param keyType The data type of the keys. 271 * @param valueType The data type of the values. 272 * @param valueTypeArgs The generic type arguments of the data type of the values. 273 * @return The converted value, or <jk>null</jk> if the input was null. 274 */ 275 @SuppressWarnings("unchecked") 276 public static <K,V> Map<K,V> addToMap(Map<K,V> appendTo, Object[] values, Class<K> keyType, Class<V> valueType, Type...valueTypeArgs) { 277 if (values == null) 278 return appendTo; 279 try { 280 Map<K,V> m = appendTo; 281 if (m == null) 282 m = new LinkedHashMap<>(); 283 for (Object o : values) { 284 if (o != null) { 285 if (isObjectMap(o, false)) { 286 for (Map.Entry<String,Object> e : new ObjectMap(o.toString()).entrySet()) 287 m.put(toType(e.getKey(), keyType), toType(e.getValue(), valueType, valueTypeArgs)); 288 } else if (o instanceof Map) { 289 for (Map.Entry<Object,Object> e : ((Map<Object,Object>)o).entrySet()) 290 m.put(toType(e.getKey(), keyType), toType(e.getValue(), valueType, valueTypeArgs)); 291 } else { 292 throw new FormattedRuntimeException("Invalid object type {0} passed to addToMap()", o.getClass().getName()); 293 } 294 } 295 } 296 return m.isEmpty() ? null : m; 297 } catch (ParseException e) { 298 throw new RuntimeException(e); 299 } 300 } 301 302 /** 303 * Creates a new list from the specified collection. 304 * 305 * @param val The value to copy from. 306 * @return A new {@link ArrayList}, or <jk>null</jk> if the input was null. 307 */ 308 public static <T> List<T> newList(Collection<T> val) { 309 if (val == null) 310 return null; 311 return new ArrayList<>(val); 312 } 313 314 /** 315 * Copies the specified values into an existing list. 316 * 317 * @param l 318 * The list to add to. 319 * <br>If <jk>null</jk>, a new {@link ArrayList} will be created. 320 * @param val The values to add. 321 * @return The list with values copied into it. 322 */ 323 public static <T> List<T> addToList(List<T> l, Collection<T> val) { 324 if (val != null) { 325 if (l == null) 326 l = new ArrayList<>(val); 327 else 328 l.addAll(val); 329 } 330 return l; 331 } 332 333 /** 334 * Creates a new map from the specified map. 335 * 336 * @param val The value to copy from. 337 * @return A new {@link LinkedHashMap}, or <jk>null</jk> if the input was null. 338 */ 339 public static <K,V> Map<K,V> newMap(Map<K,V> val) { 340 if (val == null) 341 return null; 342 return new LinkedHashMap<>(val); 343 } 344 345 /** 346 * Copies the specified values into an existing map. 347 * 348 * @param m 349 * The map to add to. 350 * <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created. 351 * @param val The values to add. 352 * @return The list with values copied into it. 353 */ 354 public static <K,V> Map<K,V> addToMap(Map<K,V> m, Map<K,V> val) { 355 if (val != null) { 356 if (m == null) 357 m = new LinkedHashMap<>(val); 358 else 359 m.putAll(val); 360 } 361 return m; 362 } 363 364 /** 365 * Adds a single entry into an existing map. 366 * 367 * @param m 368 * The map to add to. 369 * <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created. 370 * @param key The entry key. 371 * @param value The entry value. 372 * @return The list with values copied into it. 373 */ 374 public static <K,V> Map<K,V> addToMap(Map<K,V> m, K key, V value) { 375 if (m == null) 376 m = new LinkedHashMap<>(); 377 m.put(key, value); 378 return m; 379 } 380 381 /** 382 * Creates a new map from the specified map. 383 * 384 * @param val The value to copy from. 385 * @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering. 386 * @return A new {@link LinkedHashMap}, or <jk>null</jk> if the input was null. 387 */ 388 public static <K,V> Map<K,V> newSortedMap(Map<K,V> val, Comparator<K> comparator) { 389 if (val == null) 390 return null; 391 Map<K,V> m = new TreeMap<>(comparator); 392 m.putAll(val); 393 return m; 394 } 395 396 /** 397 * Creates a case-insensitive ordered set out of the specified string values. 398 * 399 * @param values The values to populate the set with. 400 * @return A new ordered set. 401 */ 402 public static Set<String> newSortedCaseInsensitiveSet(String...values) { 403 Set<String> s = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER) { 404 private static final long serialVersionUID = 1L; 405 @Override 406 public boolean contains(Object v) { 407 return v == null ? false : super.contains(v); 408 } 409 }; 410 for (String v : values) 411 if (v != null) 412 s.add(v); 413 return s; 414 } 415 416 /** 417 * Creates a case-insensitive ordered set out of the specified string values. 418 * 419 * @param values 420 * A comma-delimited list of the values to populate the set with. 421 * @return A new ordered set. 422 */ 423 public static Set<String> newSortedCaseInsensitiveSet(String values) { 424 return newSortedCaseInsensitiveSet(StringUtils.split(StringUtils.emptyIfNull(values))); 425 } 426 427 /** 428 * Same as {@link #newSortedCaseInsensitiveSet(String)} but makes the set unmodifiable. 429 * 430 * @param values 431 * A comma-delimited list of the values to populate the set with. 432 * @return A new ordered set. 433 */ 434 public static Set<String> newUnmodifiableSortedCaseInsensitiveSet(String values) { 435 return Collections.unmodifiableSet(newSortedCaseInsensitiveSet(StringUtils.split(StringUtils.emptyIfNull(values)))); 436 } 437 438 /** 439 * Copies the specified values into an existing map. 440 * 441 * @param m 442 * The map to add to. 443 * <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created. 444 * @param val The values to add. 445 * @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering. 446 * @return The list with values copied into it. 447 */ 448 public static <K,V> Map<K,V> addToSortedMap(Map<K,V> m, Map<K,V> val, Comparator<K> comparator) { 449 if (val != null) { 450 if (m == null) { 451 m = new TreeMap<>(comparator); 452 m.putAll(val); 453 } else { 454 m.putAll(val); 455 } 456 } 457 return m; 458 } 459 460 /** 461 * Adds a single entry into an existing map. 462 * 463 * @param m 464 * The map to add to. 465 * <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created. 466 * @param key The entry key. 467 * @param value The entry value. 468 * @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering. 469 * @return The list with values copied into it. 470 */ 471 public static <K,V> Map<K,V> addToSortedMap(Map<K,V> m, K key, V value, Comparator<K> comparator) { 472 if (m == null) 473 m = new TreeMap<>(comparator); 474 m.put(key, value); 475 return m; 476 } 477 478 /** 479 * Converts the specified arguments into an unmodifiable {@link HashSet}. 480 * 481 * @param values The entries to populate the hashset with. 482 * @return A new {@link HashSet} populated with the specified arguments. 483 */ 484 @SafeVarargs 485 public static <T> Set<T> newUnmodifiableHashSet(T...values) { 486 return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(values))); 487 } 488}