View Javadoc
1   /**
2    * Copyright 2010-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.common.util;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.io.StringReader;
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.Collection;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.Iterator;
27  import java.util.LinkedHashSet;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  import java.util.TreeSet;
32  
33  import org.apache.commons.io.IOUtils;
34  import org.apache.commons.lang3.StringUtils;
35  import org.kuali.common.util.nullify.NullUtils;
36  
37  import com.google.common.collect.ImmutableList;
38  
39  public class CollectionUtils {
40  
41  	public static Collection<String> getBlanks(Collection<String> collection) {
42  		Collection<String> blanks = new ArrayList<String>();
43  		for (String element : collection) {
44  			if (StringUtils.isBlank(element)) {
45  				blanks.add(element);
46  			}
47  		}
48  		return blanks;
49  	}
50  
51  	/**
52  	 * Returns a new unmodifiable list containing the elements from <code>list</code>
53  	 * 
54  	 * @deprecated See ListUtils.newArrayList() instead
55  	 */
56  	@Deprecated
57  	public static <T> List<T> unmodifiableCopy(List<T> list) {
58  		return Collections.unmodifiableList(new ArrayList<T>(list));
59  	}
60  
61  	/**
62  	 * Get an unmodifiable list from the single element. Return emptyList() if element is null.
63  	 * 
64  	 * @deprecated Use CollectionUtils.singletonList() instead
65  	 */
66  	@Deprecated
67  	public static <T> List<T> unmodifiableList(T element) {
68  		List<T> list = toEmptyList(element);
69  		return Collections.unmodifiableList(list);
70  	}
71  
72  	/**
73  	 * Get an unmodifiable list from elements
74  	 * 
75  	 * @deprecated Use ImmutableList.copyOf(elements) instead
76  	 */
77  	@Deprecated
78  	public static <T> List<T> unmodifiableList(T... elements) {
79  		return Collections.unmodifiableList(Arrays.asList(elements));
80  	}
81  
82  	/**
83  	 * If the CSV is whitespace, the empty string, <code>null</code>, <code>"null"</code>, or <code>"none"</code>, return an empty list.
84  	 */
85  	public static List<String> getNoneSensitiveListFromCSV(String csv) {
86  		if (StringUtils.isBlank(csv) || NullUtils.isNullOrNone(csv)) {
87  			return Collections.<String> emptyList();
88  		} else {
89  			return CollectionUtils.getTrimmedListFromCSV(csv);
90  		}
91  	}
92  
93  	/**
94  	 * Remove any Strings from the list that do not match the filter and then sort the ones that remain
95  	 * 
96  	 * @return The list of strings that were filtered out.
97  	 * @deprecated
98  	 */
99  	@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.&lt;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 }