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.ConverterUtils.*; 017 018import java.lang.reflect.*; 019import java.util.*; 020 021import org.apache.juneau.*; 022import org.apache.juneau.collections.*; 023import org.apache.juneau.parser.*; 024 025/** 026 * Utility methods for collections. 027 */ 028public final class CollectionUtils { 029 030 /** 031 * Add a value to a list if the value is not null. 032 * 033 * @param l The list to add to. 034 * @param o The element to add. 035 * @return The same list. 036 */ 037 public static <T> List<T> addIfNotNull(List<T> l, T o) { 038 if (o != null) 039 l.add(o); 040 return l; 041 } 042 043 /** 044 * Returns an iterable over the specified enumeration. 045 * 046 * @param e The collection to iterate over. 047 * @return An iterable over the enumeration. 048 */ 049 public static <E> Iterable<E> iterable(final Enumeration<E> e) { 050 if (e == null) 051 return null; 052 return new Iterable<E>() { 053 @Override 054 public Iterator<E> iterator() { 055 return new Iterator<E>() { 056 @Override 057 public boolean hasNext() { 058 return e.hasMoreElements(); 059 } 060 @Override 061 public E next() { 062 return e.nextElement(); 063 } 064 @Override 065 public void remove() { 066 throw new UnsupportedOperationException(); 067 } 068 }; 069 } 070 }; 071 } 072 073 /** 074 * Creates an iterator over a list of iterable objects. 075 * 076 * @param <E> The element type. 077 * @param l The iterables to iterate over. 078 * @return A new iterator. 079 */ 080 public static <E> Iterator<E> iterator(final List<Iterable<E>> l) { 081 return new Iterator<E>() { 082 Iterator<Iterable<E>> i1 = l.iterator(); 083 Iterator<E> i2 = i1.hasNext() ? i1.next().iterator() : null; 084 085 @Override /* Iterator */ 086 public boolean hasNext() { 087 while (i2 != null && ! i2.hasNext()) 088 i2 = (i1.hasNext() ? i1.next().iterator() : null); 089 return (i2 != null); 090 } 091 092 @Override /* Iterator */ 093 public E next() { 094 hasNext(); 095 if (i2 == null) 096 throw new NoSuchElementException(); 097 return i2.next(); 098 } 099 100 @Override /* Iterator */ 101 public void remove() { 102 if (i2 == null) 103 throw new NoSuchElementException(); 104 i2.remove(); 105 } 106 }; 107 } 108 109 /** 110 * Adds a set of values to an existing list. 111 * 112 * @param appendTo 113 * The list to append to. 114 * <br>If <jk>null</jk>, a new {@link ArrayList} will be created. 115 * @param values The values to add. 116 * @param type The data type of the elements. 117 * @param args The generic type arguments of the data type. 118 * @return The converted value, or <jk>null</jk> if the input was null. 119 */ 120 public static <T> List<T> addToList(List<T> appendTo, Object[] values, Class<T> type, Type...args) { 121 if (values == null) 122 return appendTo; 123 try { 124 List<T> l = appendTo; 125 if (appendTo == null) 126 l = new ArrayList<>(); 127 for (Object o : values) { 128 if (o != null) { 129 if (isJsonArray(o, false)) { 130 for (Object o2 : new OList(o.toString())) 131 l.add(toType(o2, type, args)); 132 } else if (o instanceof Collection) { 133 for (Object o2 : (Collection<?>)o) 134 l.add(toType(o2, type, args)); 135 } else if (o.getClass().isArray()) { 136 for (int i = 0; i < Array.getLength(o); i++) 137 l.add(toType(Array.get(o, i), type, args)); 138 } else { 139 l.add(toType(o, type, args)); 140 } 141 } 142 } 143 return l.isEmpty() ? null : l; 144 } catch (ParseException e) { 145 throw new RuntimeException(e); 146 } 147 } 148 149 /** 150 * Adds a set of values to an existing map. 151 * 152 * @param appendTo 153 * The map to append to. 154 * <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created. 155 * @param values The values to add. 156 * @param keyType The data type of the keys. 157 * @param valueType The data type of the values. 158 * @param valueTypeArgs The generic type arguments of the data type of the values. 159 * @return The converted value, or <jk>null</jk> if the input was null. 160 */ 161 @SuppressWarnings("unchecked") 162 public static <K,V> Map<K,V> addToMap(Map<K,V> appendTo, Object[] values, Class<K> keyType, Class<V> valueType, Type...valueTypeArgs) { 163 if (values == null) 164 return appendTo; 165 try { 166 Map<K,V> m = appendTo; 167 if (m == null) 168 m = new LinkedHashMap<>(); 169 for (Object o : values) { 170 if (o != null) { 171 if (isJsonObject(o, false)) { 172 for (Map.Entry<String,Object> e : OMap.ofJson(o.toString()).entrySet()) 173 m.put(toType(e.getKey(), keyType), toType(e.getValue(), valueType, valueTypeArgs)); 174 } else if (o instanceof Map) { 175 for (Map.Entry<Object,Object> e : ((Map<Object,Object>)o).entrySet()) 176 m.put(toType(e.getKey(), keyType), toType(e.getValue(), valueType, valueTypeArgs)); 177 } else { 178 throw new BasicRuntimeException("Invalid object type {0} passed to addToMap()", o.getClass().getName()); 179 } 180 } 181 } 182 return m.isEmpty() ? null : m; 183 } catch (ParseException e) { 184 throw new RuntimeException(e); 185 } 186 } 187 188 /** 189 * Creates a new list from the specified collection. 190 * 191 * @param val The value to copy from. 192 * @return A new {@link ArrayList}, or <jk>null</jk> if the input was null. 193 */ 194 public static <T> AList<T> newList(Collection<T> val) { 195 return AList.nullable(val); 196 } 197 198 /** 199 * Copies the specified values into an existing list. 200 * 201 * @param l 202 * The list to add to. 203 * <br>If <jk>null</jk>, a new {@link ArrayList} will be created. 204 * @param val The values to add. 205 * @return The list with values copied into it. 206 */ 207 public static <T> List<T> addToList(List<T> l, Collection<T> val) { 208 if (val != null) { 209 if (l == null) 210 l = new ArrayList<>(val); 211 else 212 l.addAll(val); 213 } 214 return l; 215 } 216 217 /** 218 * Creates a new map from the specified map. 219 * 220 * @param val The value to copy from. 221 * @return A new {@link LinkedHashMap}, or <jk>null</jk> if the input was null. 222 */ 223 public static <K,V> Map<K,V> newMap(Map<K,V> val) { 224 if (val == null) 225 return null; 226 return new LinkedHashMap<>(val); 227 } 228 229 /** 230 * Copies the specified values into an existing map. 231 * 232 * @param m 233 * The map to add to. 234 * <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created. 235 * @param val The values to add. 236 * @return The list with values copied into it. 237 */ 238 public static <K,V> Map<K,V> addToMap(Map<K,V> m, Map<K,V> val) { 239 if (val != null) { 240 if (m == null) 241 m = new LinkedHashMap<>(val); 242 else 243 m.putAll(val); 244 } 245 return m; 246 } 247 248 /** 249 * Adds a single entry into an existing map. 250 * 251 * @param m 252 * The map to add to. 253 * <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created. 254 * @param key The entry key. 255 * @param value The entry value. 256 * @return The list with values copied into it. 257 */ 258 public static <K,V> Map<K,V> addToMap(Map<K,V> m, K key, V value) { 259 if (m == null) 260 m = new LinkedHashMap<>(); 261 m.put(key, value); 262 return m; 263 } 264 265 /** 266 * Creates a new map from the specified map. 267 * 268 * @param val The value to copy from. 269 * @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering. 270 * @return A new {@link LinkedHashMap}, or <jk>null</jk> if the input was null. 271 */ 272 public static <K,V> Map<K,V> newSortedMap(Map<K,V> val, Comparator<K> comparator) { 273 if (val == null) 274 return null; 275 Map<K,V> m = new TreeMap<>(comparator); 276 m.putAll(val); 277 return m; 278 } 279 280 /** 281 * Creates a case-insensitive ordered set out of the specified string values. 282 * 283 * @param values The values to populate the set with. 284 * @return A new ordered set. 285 */ 286 public static Set<String> newSortedCaseInsensitiveSet(String...values) { 287 Set<String> s = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER) { 288 private static final long serialVersionUID = 1L; 289 @Override 290 public boolean contains(Object v) { 291 return v == null ? false : super.contains(v); 292 } 293 }; 294 for (String v : values) 295 if (v != null) 296 s.add(v); 297 return s; 298 } 299 300 /** 301 * Creates a case-insensitive ordered set out of the specified string values. 302 * 303 * @param values 304 * A comma-delimited list of the values to populate the set with. 305 * @return A new ordered set. 306 */ 307 public static Set<String> newSortedCaseInsensitiveSet(String values) { 308 return newSortedCaseInsensitiveSet(StringUtils.split(StringUtils.emptyIfNull(values))); 309 } 310 311 /** 312 * Same as {@link #newSortedCaseInsensitiveSet(String)} but makes the set unmodifiable. 313 * 314 * @param values 315 * A comma-delimited list of the values to populate the set with. 316 * @return A new ordered set. 317 */ 318 public static Set<String> newUnmodifiableSortedCaseInsensitiveSet(String values) { 319 return Collections.unmodifiableSet(newSortedCaseInsensitiveSet(StringUtils.split(StringUtils.emptyIfNull(values)))); 320 } 321 322 /** 323 * Copies the specified values into an existing map. 324 * 325 * @param m 326 * The map to add to. 327 * <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created. 328 * @param val The values to add. 329 * @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering. 330 * @return The list with values copied into it. 331 */ 332 public static <K,V> Map<K,V> addToSortedMap(Map<K,V> m, Map<K,V> val, Comparator<K> comparator) { 333 if (val != null) { 334 if (m == null) { 335 m = new TreeMap<>(comparator); 336 m.putAll(val); 337 } else { 338 m.putAll(val); 339 } 340 } 341 return m; 342 } 343 344 /** 345 * Adds a single entry into an existing map. 346 * 347 * @param m 348 * The map to add to. 349 * <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created. 350 * @param key The entry key. 351 * @param value The entry value. 352 * @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering. 353 * @return The list with values copied into it. 354 */ 355 public static <K,V> Map<K,V> addToSortedMap(Map<K,V> m, K key, V value, Comparator<K> comparator) { 356 if (m == null) 357 m = new TreeMap<>(comparator); 358 m.put(key, value); 359 return m; 360 } 361 362 /** 363 * Simple passthrough to {@link Collections#emptySet()} 364 * 365 * @return A new unmodifiable empty set. 366 */ 367 public static <T> Set<T> emptySet() { 368 return Collections.emptySet(); 369 } 370 371 /** 372 * Simple passthrough to {@link Collections#emptyList()} 373 * 374 * @return A new unmodifiable empty list. 375 */ 376 public static <T> List<T> emptyList() { 377 return Collections.emptyList(); 378 } 379 380 /** 381 * Simple passthrough to {@link Collections#emptyMap()} 382 * 383 * @return A new unmodifiable empty set. 384 */ 385 public static <K,V> Map<K,V> emptyMap() { 386 return Collections.emptyMap(); 387 } 388 389 /** 390 * Returns the last entry in a list. 391 * 392 * @param <T> The element type. 393 * @param l The list. 394 * @return The last element, or <jk>null</jk> if the list is <jk>null</jk> or empty. 395 */ 396 public static <T> T last(List<T> l) { 397 if (l == null || l.isEmpty()) 398 return null; 399 return l.get(l.size()-1); 400 } 401}