001/** 002 * Copyright 2010-2013 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.common.util; 017 018import java.io.File; 019import java.io.IOException; 020import java.io.StringReader; 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.Collection; 024import java.util.Collections; 025import java.util.HashMap; 026import java.util.Iterator; 027import java.util.LinkedHashSet; 028import java.util.List; 029import java.util.Map; 030import java.util.Set; 031import java.util.TreeSet; 032 033import org.apache.commons.io.IOUtils; 034import org.apache.commons.lang3.StringUtils; 035import org.kuali.common.util.nullify.NullUtils; 036 037import com.google.common.collect.ImmutableList; 038 039public class CollectionUtils { 040 041 public static Collection<String> getBlanks(Collection<String> collection) { 042 Collection<String> blanks = new ArrayList<String>(); 043 for (String element : collection) { 044 if (StringUtils.isBlank(element)) { 045 blanks.add(element); 046 } 047 } 048 return blanks; 049 } 050 051 /** 052 * Returns a new unmodifiable list containing the elements from <code>list</code> 053 * 054 * @deprecated See ListUtils.newArrayList() instead 055 */ 056 @Deprecated 057 public static <T> List<T> unmodifiableCopy(List<T> list) { 058 return Collections.unmodifiableList(new ArrayList<T>(list)); 059 } 060 061 /** 062 * Get an unmodifiable list from the single element. Return emptyList() if element is null. 063 * 064 * @deprecated Use CollectionUtils.singletonList() instead 065 */ 066 @Deprecated 067 public static <T> List<T> unmodifiableList(T element) { 068 List<T> list = toEmptyList(element); 069 return Collections.unmodifiableList(list); 070 } 071 072 /** 073 * Get an unmodifiable list from elements 074 * 075 * @deprecated Use ImmutableList.copyOf(elements) instead 076 */ 077 @Deprecated 078 public static <T> List<T> unmodifiableList(T... elements) { 079 return Collections.unmodifiableList(Arrays.asList(elements)); 080 } 081 082 /** 083 * If the CSV is whitespace, the empty string, <code>null</code>, <code>"null"</code>, or <code>"none"</code>, return an empty list. 084 */ 085 public static List<String> getNoneSensitiveListFromCSV(String csv) { 086 if (StringUtils.isBlank(csv) || NullUtils.isNullOrNone(csv)) { 087 return Collections.<String> emptyList(); 088 } else { 089 return CollectionUtils.getTrimmedListFromCSV(csv); 090 } 091 } 092 093 /** 094 * Remove any Strings from the list that do not match the filter and then sort the ones that remain 095 * 096 * @return The list of strings that were filtered out. 097 * @deprecated 098 */ 099 @Deprecated 100 public static List<String> filterAndSort(List<String> strings, StringFilter filter) { 101 List<String> excluded = filter(strings, filter); 102 Collections.sort(strings); 103 return excluded; 104 } 105 106 /** 107 * Remove any Strings from the list that do not match the filter and then sort the ones that remain 108 * 109 * @return The list of strings that were filtered out. 110 */ 111 public static List<String> filterAndSortStrings(List<String> strings, org.kuali.common.util.filter.StringFilter filter) { 112 List<String> excluded = filterStrings(strings, filter); 113 Collections.sort(strings); 114 return excluded; 115 } 116 117 /** 118 * Remove any Strings from the collection that do not match the filter 119 */ 120 public static List<String> filterStrings(Collection<String> strings, org.kuali.common.util.filter.StringFilter filter) { 121 List<String> excluded = new ArrayList<String>(); 122 Iterator<String> itr = strings.iterator(); 123 while (itr.hasNext()) { 124 String string = itr.next(); 125 if (!filter.include(string)) { 126 excluded.add(string); 127 itr.remove(); 128 } 129 } 130 return excluded; 131 } 132 133 /** 134 * Remove any Strings from the collection that do not match the filter 135 * 136 * @deprecated 137 */ 138 @Deprecated 139 public static List<String> filter(Collection<String> strings, StringFilter filter) { 140 List<String> excluded = new ArrayList<String>(); 141 Iterator<String> itr = strings.iterator(); 142 while (itr.hasNext()) { 143 String string = itr.next(); 144 if (!filter.include(string)) { 145 excluded.add(string); 146 itr.remove(); 147 } 148 } 149 return excluded; 150 } 151 152 /** 153 * Null safe method for converting an array of objects into a list. Never returns null. 154 */ 155 public static List<Object> asList(Object... objects) { 156 List<Object> list = new ArrayList<Object>(); 157 if (objects == null) { 158 return list; 159 } 160 for (Object element : objects) { 161 if (element != null) { 162 list.add(element); 163 } 164 } 165 return list; 166 } 167 168 /** 169 * Null safe method for converting an untyped array of classes into a list. Never returns null. 170 */ 171 public static List<Class<?>> asList(Class<?>... classes) { 172 List<Class<?>> list = new ArrayList<Class<?>>(); 173 if (classes == null) { 174 return list; 175 } 176 for (Class<?> element : classes) { 177 if (element != null) { 178 list.add(element); 179 } 180 } 181 return list; 182 } 183 184 /** 185 * Return an array of int's that represents as even of a split as possible 186 * 187 * For example: passing in 100,7 returns 15, 15, 14, 14, 14, 14, 14 188 * 189 * @param numerator 190 * @param denominator 191 * @return 192 */ 193 public static int[] getDivideEvenly(int number, int howManyWays) { 194 Assert.isTrue(howManyWays > 0, "howManyWays must be a positive integer"); 195 int quotient = number / howManyWays; 196 int remainder = number % howManyWays; 197 198 int[] lengths = new int[howManyWays]; 199 for (int i = 0; i < howManyWays; i++) { 200 int length = i < remainder ? quotient + 1 : quotient; 201 lengths[i] = length; 202 } 203 return lengths; 204 } 205 206 /** 207 * Split <code>elements</code> evenly into separate lists divided up <code>howManyWays</code> 208 */ 209 public static final <T> List<List<T>> splitEvenly(List<T> elements, int howManyWays) { 210 // Can't split 2 things 3 ways 211 if (howManyWays > elements.size()) { 212 howManyWays = elements.size(); 213 } 214 int[] lengths = getDivideEvenly(elements.size(), howManyWays); 215 int offset = 0; 216 List<List<T>> listOfLists = new ArrayList<List<T>>(); 217 for (int i = 0; i < lengths.length; i++) { 218 int length = lengths[i]; 219 List<T> sublist = new ArrayList<T>(); 220 for (int j = offset; j < offset + length; j++) { 221 sublist.add(elements.get(j)); 222 } 223 listOfLists.add(sublist); 224 offset += length; 225 } 226 return listOfLists; 227 } 228 229 /** 230 * Prefix the strings passed in with their position in the list (left padded with zero's). The padding is the number of digits in the size of the list. A list with 100 elements 231 * will return strings prefixed with 000, 001, etc. 232 */ 233 public static final List<String> getSequencedStrings(List<String> strings, int initialSequenceNumber) { 234 List<String> sequencedStrings = new ArrayList<String>(); 235 int size = strings.size(); 236 int length = new Integer(size).toString().length(); 237 String prefix = StringUtils.repeat("0", length); 238 for (String string : strings) { 239 String sequence = StringUtils.right(prefix + (initialSequenceNumber++), length); 240 String sequencedString = sequence + "-" + string; 241 sequencedStrings.add(sequencedString); 242 } 243 return sequencedStrings; 244 } 245 246 /** 247 * Prefix the strings passed in with their position in the list (left padded with zero's). The padding is the number of digits in the size of the list. A list with 100 elements 248 * will return strings prefixed with 000, 001, etc. 249 */ 250 public static final List<String> getSequencedStrings(List<String> strings) { 251 return getSequencedStrings(strings, 0); 252 } 253 254 /** 255 * Return a new <code>List</code> containing the unique set of strings from <code>strings</code> 256 */ 257 public static final List<String> getUniqueStrings(List<String> strings) { 258 LinkedHashSet<String> unique = new LinkedHashSet<String>(strings); 259 return new ArrayList<String>(unique); 260 } 261 262 public static final List<File> getUniqueFiles(List<File> files) { 263 LinkedHashSet<File> unique = new LinkedHashSet<File>(files); 264 return new ArrayList<File>(unique); 265 } 266 267 public static final List<String> getLines(String s) { 268 if (s == null) { 269 return Collections.<String> emptyList(); 270 } 271 try { 272 return IOUtils.readLines(new StringReader(s)); 273 } catch (IOException e) { 274 throw new IllegalStateException(e); 275 } 276 } 277 278 /** 279 * Return a new list containing the unique set of strings contained in both lists 280 */ 281 public static final List<String> combineStringsUniquely(List<String> list1, List<String> list2) { 282 List<String> newList = getUniqueStrings(list1); 283 for (String element : list2) { 284 if (!newList.contains(element)) { 285 newList.add(element); 286 } 287 } 288 return newList; 289 } 290 291 protected static final <T> T getNewInstance(Class<T> c) { 292 try { 293 return c.newInstance(); 294 } catch (IllegalAccessException e) { 295 throw new IllegalArgumentException(e); 296 } catch (InstantiationException e) { 297 throw new IllegalArgumentException(e); 298 } 299 } 300 301 /** 302 * Create a new list containing new instances of <code>c</code> 303 */ 304 public static final <T> List<T> getNewList(Class<T> c, int size) { 305 List<T> list = new ArrayList<T>(); 306 for (int i = 0; i < size; i++) { 307 T element = getNewInstance(c); 308 list.add(element); 309 } 310 return list; 311 } 312 313 /** 314 * Return a list containing only the elements where the corresponding index in the <code>includes</code> list is <code>true</code>. <code>includes</code> and <code>list</code> 315 * must be the same size. 316 */ 317 public static final <T> List<T> getList(List<Boolean> includes, List<T> list) { 318 Assert.isTrue(includes.size() == list.size()); 319 List<T> included = new ArrayList<T>(); 320 for (int i = 0; i < includes.size(); i++) { 321 if (includes.get(i)) { 322 included.add(list.get(i)); 323 } 324 } 325 return included; 326 } 327 328 /** 329 * Combine the list of lists into a single list 330 */ 331 public static final <T> List<T> combineLists(List<List<T>> listOfLists) { 332 List<T> combined = new ArrayList<T>(); 333 for (List<T> list : listOfLists) { 334 combined.addAll(list); 335 } 336 return combined; 337 } 338 339 /** 340 * Combine the list of maps into a single map 341 */ 342 public static final <K, V> Map<K, V> combineMaps(List<Map<K, V>> listOfMaps) { 343 Map<K, V> combined = new HashMap<K, V>(); 344 for (Map<K, V> map : listOfMaps) { 345 combined.putAll(map); 346 } 347 return combined; 348 } 349 350 /** 351 * Return a combined list where <code>required</code> is always the first element in the list 352 */ 353 public static final <T> List<T> combine(T element, List<T> list) { 354 Assert.notNull(element, "element is required"); 355 if (list == null) { 356 return Collections.singletonList(element); 357 } else { 358 List<T> combined = new ArrayList<T>(); 359 // Always insert required as the first element in the list 360 combined.add(element); 361 // Add the other elements 362 for (T optional : list) { 363 combined.add(optional); 364 } 365 return combined; 366 } 367 } 368 369 /** 370 * If <code>map==null</code> return emptyMap(), otherwise return <code>map</code> 371 */ 372 public static final <K, V> Map<K, V> toEmptyMap(Map<K, V> map) { 373 if (map == null) { 374 return Collections.emptyMap(); 375 } else { 376 return map; 377 } 378 } 379 380 /** 381 * If <code>map==null</code> return <code>new HashMap<K,V>()</code>, otherwise return <code>map</code> 382 */ 383 public static final <K, V> Map<K, V> toModifiableEmptyMap(Map<K, V> map) { 384 if (map == null) { 385 return new HashMap<K, V>(); 386 } else { 387 return map; 388 } 389 } 390 391 /** 392 * If <code>key==null</code> OR <code>value==null</code> return <code>new HashMap<K,V>()</code> otherwise return 393 * <code>new HashMap<K, V>(Collections.singletonMap(key, value))</code> 394 */ 395 public static final <K, V> Map<K, V> toModifiableEmptyMap(K key, V value) { 396 if (key == null || value == null) { 397 return new HashMap<K, V>(); 398 } else { 399 return new HashMap<K, V>(Collections.singletonMap(key, value)); 400 } 401 } 402 403 /** 404 * If <code>key==null</code> OR <code>value==null</code> return an empty map otherwise return a singleton map. 405 */ 406 public static final <K, V> Map<K, V> toEmptyMap(K key, V value) { 407 if (key == null || value == null) { 408 return Collections.emptyMap(); 409 } else { 410 return Collections.singletonMap(key, value); 411 } 412 } 413 414 /** 415 * If <code>o==null</code> return <code>Collections.<T> emptyList()</code> otherwise return <code>Collections.singletonList(o)</code> 416 */ 417 public static final <T> List<T> toEmptyList(T o) { 418 if (o == null) { 419 return Collections.<T> emptyList(); 420 } else { 421 return Collections.singletonList(o); 422 } 423 } 424 425 /** 426 * Returns an immutable list containing only the specified object. The returned list is serializable. 427 * 428 * @throws IllegalArgumentException 429 * if object is null 430 */ 431 public static final <T> List<T> singletonList(T o) { 432 if (o != null) { 433 return Collections.singletonList(o); 434 } else { 435 throw new IllegalArgumentException("nulls not allowed"); 436 } 437 } 438 439 /** 440 * Add keys and values to map. Keys and values must be the same size (or both null). Map cannot be null. 441 */ 442 public static final <K, V> void combine(Map<K, V> map, List<K> keys, List<V> values) { 443 keys = toEmptyList(keys); 444 values = toEmptyList(values); 445 Assert.isTrue(keys.size() == values.size(), "sizes must match"); 446 Assert.notNull(map, "map is null"); 447 for (int i = 0; i < keys.size(); i++) { 448 K key = keys.get(i); 449 V value = values.get(i); 450 map.put(key, value); 451 } 452 } 453 454 /** 455 * If <code>list==null</code> return an empty list otherwise return <code>list</code> 456 */ 457 public static final <T> List<T> toEmptyList(List<T> list) { 458 if (list == null) { 459 return Collections.emptyList(); 460 } else { 461 return list; 462 } 463 } 464 465 public static final <T> List<T> toNullIfEmpty(List<T> list) { 466 if (isEmpty(list)) { 467 return null; 468 } else { 469 return list; 470 } 471 } 472 473 public static final <T> Collection<T> toNullIfEmpty(Collection<T> c) { 474 if (isEmpty(c)) { 475 return null; 476 } else { 477 return c; 478 } 479 } 480 481 public static final <T> List<T> getPreFilledList(int size, T value) { 482 if (value == null || size < 1) { 483 return Collections.<T> emptyList(); 484 } else { 485 List<T> list = new ArrayList<T>(size); 486 for (int i = 0; i < size; i++) { 487 list.add(value); 488 } 489 return list; 490 } 491 } 492 493 public static final String getSpaceSeparatedCSV(List<String> strings) { 494 return getStringWithSeparator(strings, ", "); 495 } 496 497 public static final String getStringWithSeparator(List<?> list, String separator) { 498 list = toEmptyList(list); 499 StringBuilder sb = new StringBuilder(); 500 for (int i = 0; i < toEmptyList(list).size(); i++) { 501 if (i != 0) { 502 sb.append(separator); 503 } 504 Object element = list.get(i); 505 if (element != null) { 506 sb.append(element.toString()); 507 } else { 508 sb.append(NullUtils.NULL); 509 } 510 } 511 return sb.toString(); 512 } 513 514 public static final String toCSV(List<Integer> integers) { 515 Assert.noNulls(integers); 516 StringBuilder sb = new StringBuilder(); 517 for (int i = 0; i < integers.size(); i++) { 518 if (i != 0) { 519 sb.append(","); 520 } 521 sb.append(integers.get(i)); 522 } 523 return sb.toString(); 524 } 525 526 public static final String asCSV(List<String> strings) { 527 return getCSV(strings); 528 } 529 530 public static final String getCSV(List<String> strings) { 531 return getStringWithSeparator(strings, ","); 532 } 533 534 public static final String getSpaceSeparatedString(List<?> list) { 535 return getStringWithSeparator(list, " "); 536 } 537 538 public static final Object[] toObjectArray(List<Object> objects) { 539 return objects.toArray(new Object[objects.size()]); 540 } 541 542 public static final String[] toStringArray(List<String> strings) { 543 return strings.toArray(new String[strings.size()]); 544 } 545 546 public static final boolean isEmpty(Collection<?> c) { 547 return c == null || c.size() == 0; 548 } 549 550 public static final boolean isEmpty(Map<?, ?> m) { 551 return m == null || m.size() == 0; 552 } 553 554 public static final List<String> sortedMerge(List<String> list, String csv) { 555 Set<String> set = new TreeSet<String>(); 556 set.addAll(toEmptyList(list)); 557 set.addAll(getTrimmedListFromCSV(csv)); 558 return new ArrayList<String>(set); 559 } 560 561 public static final List<String> getTrimmedListFromCSV(String csv) { 562 if (StringUtils.isBlank(csv)) { 563 return Collections.<String> emptyList(); 564 } 565 String[] tokens = Str.splitAndTrimCSV(csv); 566 List<String> list = new ArrayList<String>(); 567 list.addAll(Arrays.asList(tokens)); 568 return list; 569 } 570 571 public static final <T> List<T> nullSafeCombine(List<T> list1, List<T> list2) { 572 List<T> combined = new ArrayList<T>(); 573 if (!isEmpty(list1)) { 574 combined.addAll(list1); 575 } 576 if (!isEmpty(list2)) { 577 combined.addAll(list2); 578 } 579 return combined; 580 } 581 582 public static final List<String> combineStrings(List<String> list1, List<String> list2, List<String> list3) { 583 List<String> combined = new ArrayList<String>(); 584 nullSafeAdd(combined, list1); 585 nullSafeAdd(combined, list2); 586 nullSafeAdd(combined, list3); 587 return combined; 588 } 589 590 /** 591 * Return a new list containing all of the strings from both lists with string added in between the strings from both lists 592 */ 593 public static final List<String> combineStrings(List<String> list1, String string, List<String> list2) { 594 return combineStrings(list1, toEmptyList(string), list2); 595 } 596 597 /** 598 * Return a new list containing all of the strings from both lists 599 */ 600 public static final List<String> combineStrings(List<String> list1, List<String> list2) { 601 return combineStrings(list1, (String) null, list2); 602 } 603 604 /** 605 * Return a new list containing all of the strings from both lists 606 */ 607 public static final List<String> combineStrings(List<String>... lists) { 608 List<String> combined = new ArrayList<String>(); 609 for (List<String> list : lists) { 610 combined.addAll(ImmutableList.copyOf(list)); 611 } 612 return combined; 613 } 614 615 /** 616 * Return a new list containing all of the elements from the lists passed in 617 */ 618 public static final <T> List<T> combine(List<T> list1, List<T> list2) { 619 return combine(list1, list2, null); 620 } 621 622 public static final <T> List<T> combine(List<T> list1, List<T> list2, List<T> list3) { 623 List<T> combined = new ArrayList<T>(); 624 combined.addAll(toEmptyList(list1)); 625 combined.addAll(toEmptyList(list2)); 626 combined.addAll(toEmptyList(list3)); 627 return combined; 628 } 629 630 public static final <T> void nullSafeAdd(List<T> list1, List<T> list2) { 631 if (list2 != null) { 632 list1.addAll(list2); 633 } 634 } 635 636 /** 637 * Return <code>true</code> if <code>s</code> contains any of the strings from <code>strings</code> 638 */ 639 public static final boolean containsAny(String s, List<String> strings) { 640 for (String string : strings) { 641 if (StringUtils.contains(s, string)) { 642 return true; 643 } 644 } 645 return false; 646 } 647}