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 */
016 package org.kuali.common.util;
017
018 import java.io.File;
019 import java.io.IOException;
020 import java.io.StringReader;
021 import java.util.ArrayList;
022 import java.util.Arrays;
023 import java.util.Collection;
024 import java.util.Collections;
025 import java.util.HashMap;
026 import java.util.List;
027 import java.util.Map;
028 import java.util.Set;
029 import java.util.TreeSet;
030
031 import org.apache.commons.io.IOUtils;
032 import org.apache.commons.lang3.StringUtils;
033
034 public class CollectionUtils {
035
036 /**
037 * Null safe method for converting an array of objects into a list. Never returns null.
038 */
039 public static List<Object> asList(Object... objects) {
040 List<Object> list = new ArrayList<Object>();
041 if (objects == null) {
042 return list;
043 }
044 for (Object element : objects) {
045 if (element != null) {
046 list.add(element);
047 }
048 }
049 return list;
050 }
051
052 /**
053 * Null safe method for converting an untyped array of classes into a list. Never returns null.
054 */
055 public static List<Class<?>> asList(Class<?>... classes) {
056 List<Class<?>> list = new ArrayList<Class<?>>();
057 if (classes == null) {
058 return list;
059 }
060 for (Class<?> element : classes) {
061 if (element != null) {
062 list.add(element);
063 }
064 }
065 return list;
066 }
067
068 /**
069 * Return an array of int's that represents as even of a split as possible
070 *
071 * For example: passing in 100,7 returns 15, 15, 14, 14, 14, 14, 14
072 *
073 * @param numerator
074 * @param denominator
075 * @return
076 */
077 public static int[] getDivideEvenly(int number, int howManyWays) {
078 Assert.isTrue(howManyWays > 0, "howManyWays must be a positive integer");
079 int quotient = number / howManyWays;
080 int remainder = number % howManyWays;
081
082 int[] lengths = new int[howManyWays];
083 for (int i = 0; i < howManyWays; i++) {
084 int length = i < remainder ? quotient + 1 : quotient;
085 lengths[i] = length;
086 }
087 return lengths;
088 }
089
090 /**
091 * Split <code>elements</code> evenly into separate lists divided up <code>howManyWays</code>
092 */
093 public static final <T> List<List<T>> splitEvenly(List<T> elements, int howManyWays) {
094 // Can't split 2 things 3 ways
095 if (howManyWays > elements.size()) {
096 howManyWays = elements.size();
097 }
098 int[] lengths = getDivideEvenly(elements.size(), howManyWays);
099 int offset = 0;
100 List<List<T>> listOfLists = new ArrayList<List<T>>();
101 for (int i = 0; i < lengths.length; i++) {
102 int length = lengths[i];
103 List<T> sublist = new ArrayList<T>();
104 for (int j = offset; j < offset + length; j++) {
105 sublist.add(elements.get(j));
106 }
107 listOfLists.add(sublist);
108 offset += length;
109 }
110 return listOfLists;
111 }
112
113 /**
114 * 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
115 * will return strings prefixed with 000, 001, etc.
116 */
117 public static final List<String> getSequencedStrings(List<String> strings, int initialSequenceNumber) {
118 List<String> sequencedStrings = new ArrayList<String>();
119 int size = strings.size();
120 int length = new Integer(size).toString().length();
121 String prefix = StringUtils.repeat("0", length);
122 for (String string : strings) {
123 String sequence = StringUtils.right(prefix + (initialSequenceNumber++), length);
124 String sequencedString = sequence + "-" + string;
125 sequencedStrings.add(sequencedString);
126 }
127 return sequencedStrings;
128 }
129
130 /**
131 * 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
132 * will return strings prefixed with 000, 001, etc.
133 */
134 public static final List<String> getSequencedStrings(List<String> strings) {
135 return getSequencedStrings(strings, 0);
136 }
137
138 /**
139 * Return a new <code>List</code> containing the unique set of strings from <code>strings</code>
140 */
141 public static final List<String> getUniqueStrings(List<String> strings) {
142 List<String> unique = new ArrayList<String>();
143 for (String string : strings) {
144 if (!unique.contains(string)) {
145 unique.add(string);
146 }
147 }
148 return unique;
149 }
150
151 public static final List<File> getUniqueFiles(List<File> files) {
152 List<String> strings = new ArrayList<String>();
153 for (File file : files) {
154 strings.add(LocationUtils.getCanonicalPath(file));
155 }
156 List<String> uniqueStrings = getUniqueStrings(strings);
157 List<File> uniqueFiles = new ArrayList<File>();
158 for (String uniqueString : uniqueStrings) {
159 uniqueFiles.add(new File(uniqueString));
160 }
161 return uniqueFiles;
162 }
163
164 public static final List<String> getLines(String s) {
165 if (s == null) {
166 return Collections.<String> emptyList();
167 }
168 try {
169 return IOUtils.readLines(new StringReader(s));
170 } catch (IOException e) {
171 throw new IllegalStateException(e);
172 }
173 }
174
175 /**
176 * Return a new list containing the unique set of strings contained in both lists
177 */
178 public static final List<String> combineStringsUniquely(List<String> list1, List<String> list2) {
179 List<String> newList = getUniqueStrings(list1);
180 for (String element : list2) {
181 if (!newList.contains(element)) {
182 newList.add(element);
183 }
184 }
185 return newList;
186 }
187
188 protected static final <T> T getNewInstance(Class<T> c) {
189 try {
190 return c.newInstance();
191 } catch (IllegalAccessException e) {
192 throw new IllegalArgumentException(e);
193 } catch (InstantiationException e) {
194 throw new IllegalArgumentException(e);
195 }
196 }
197
198 /**
199 * Create a new list containing new instances of <code>c</code>
200 */
201 public static final <T> List<T> getNewList(Class<T> c, int size) {
202 List<T> list = new ArrayList<T>();
203 for (int i = 0; i < size; i++) {
204 T element = getNewInstance(c);
205 list.add(element);
206 }
207 return list;
208 }
209
210 /**
211 * 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>
212 * must be the same size.
213 */
214 public static final <T> List<T> getList(List<Boolean> includes, List<T> list) {
215 Assert.isTrue(includes.size() == list.size());
216 List<T> included = new ArrayList<T>();
217 for (int i = 0; i < includes.size(); i++) {
218 if (includes.get(i)) {
219 included.add(list.get(i));
220 }
221 }
222 return included;
223 }
224
225 /**
226 * Combine the list of lists into a single list
227 */
228 public static final <T> List<T> combineLists(List<List<T>> listOfLists) {
229 List<T> combined = new ArrayList<T>();
230 for (List<T> list : listOfLists) {
231 combined.addAll(list);
232 }
233 return combined;
234 }
235
236 /**
237 * Combine the list of maps into a single map
238 */
239 public static final <K, V> Map<K, V> combineMaps(List<Map<K, V>> listOfMaps) {
240 Map<K, V> combined = new HashMap<K, V>();
241 for (Map<K, V> map : listOfMaps) {
242 combined.putAll(map);
243 }
244 return combined;
245 }
246
247 /**
248 * Return a combined list where <code>required</code> is always the first element in the list
249 */
250 public static final <T> List<T> combine(T element, List<T> list) {
251 Assert.notNull(element, "element is required");
252 if (list == null) {
253 return Collections.singletonList(element);
254 } else {
255 List<T> combined = new ArrayList<T>();
256 // Always insert required as the first element in the list
257 combined.add(element);
258 // Add the other elements
259 for (T optional : list) {
260 combined.add(optional);
261 }
262 return combined;
263 }
264 }
265
266 /**
267 * If <code>o==null</code> return an empty list otherwise return a singleton list.
268 */
269 public static final <T> List<T> toEmptyList(T o) {
270 if (o == null) {
271 return Collections.<T> emptyList();
272 } else {
273 return Collections.singletonList(o);
274 }
275 }
276
277 /**
278 * If <code>list==null</code> return an empty list otherwise return <code>list</code>
279 */
280 public static final <T> List<T> toEmptyList(List<T> list) {
281 if (list == null) {
282 return Collections.<T> emptyList();
283 } else {
284 return list;
285 }
286 }
287
288 public static final <T> List<T> toNullIfEmpty(List<T> list) {
289 if (isEmpty(list)) {
290 return null;
291 } else {
292 return list;
293 }
294 }
295
296 public static final <T> Collection<T> toNullIfEmpty(Collection<T> c) {
297 if (isEmpty(c)) {
298 return null;
299 } else {
300 return c;
301 }
302 }
303
304 public static final <T> List<T> getPreFilledList(int size, T value) {
305 if (value == null || size < 1) {
306 return Collections.<T> emptyList();
307 } else {
308 List<T> list = new ArrayList<T>(size);
309 for (int i = 0; i < size; i++) {
310 list.add(value);
311 }
312 return list;
313 }
314 }
315
316 public static final String getCSV(List<String> strings) {
317 StringBuilder sb = new StringBuilder();
318 for (int i = 0; i < toEmptyList(strings).size(); i++) {
319 if (i != 0) {
320 sb.append(",");
321 }
322 sb.append(strings.get(i));
323 }
324 return sb.toString();
325 }
326
327 public static final String getSpaceSeparatedString(List<?> list) {
328 list = toEmptyList(list);
329 StringBuilder sb = new StringBuilder();
330 for (int i = 0; i < list.size(); i++) {
331 if (i != 0) {
332 sb.append(" ");
333 }
334 sb.append(list.get(i).toString());
335 }
336 return sb.toString();
337 }
338
339 public static final Object[] toObjectArray(List<Object> objects) {
340 return objects.toArray(new Object[objects.size()]);
341 }
342
343 public static final String[] toStringArray(List<String> strings) {
344 return strings.toArray(new String[strings.size()]);
345 }
346
347 public static final boolean isEmpty(Collection<?> c) {
348 return c == null || c.size() == 0;
349 }
350
351 public static final List<String> sortedMerge(List<String> list, String csv) {
352 Set<String> set = new TreeSet<String>();
353 set.addAll(toEmptyList(list));
354 set.addAll(getTrimmedListFromCSV(csv));
355 return new ArrayList<String>(set);
356 }
357
358 public static final List<String> getTrimmedListFromCSV(String csv) {
359 if (StringUtils.isBlank(csv)) {
360 return Collections.<String> emptyList();
361 }
362 List<String> list = new ArrayList<String>();
363 String[] tokens = Str.splitAndTrimCSV(csv);
364 list.addAll(Arrays.asList(tokens));
365 return list;
366 }
367
368 public static final List<String> combineStrings(List<String> list1, List<String> list2, List<String> list3) {
369 List<String> combined = new ArrayList<String>();
370 nullSafeAdd(combined, list1);
371 nullSafeAdd(combined, list2);
372 nullSafeAdd(combined, list3);
373 return combined;
374 }
375
376 /**
377 * Return a new list containing all of the strings from both lists with string added in between the strings from both lists
378 */
379 public static final List<String> combineStrings(List<String> list1, String string, List<String> list2) {
380 return combineStrings(list1, toEmptyList(string), list2);
381 }
382
383 /**
384 * Return a new list containing all of the strings from both lists
385 */
386 public static final List<String> combineStrings(List<String> list1, List<String> list2) {
387 return combineStrings(list1, (String) null, list2);
388 }
389
390 public static final <T> void nullSafeAdd(List<T> list1, List<T> list2) {
391 if (list2 != null) {
392 list1.addAll(list2);
393 }
394 }
395
396 /**
397 * Return <code>true</code> if <code>s</code> contains any of the strings from <code>strings</code>
398 */
399 public static final boolean containsAny(String s, List<String> strings) {
400 for (String string : strings) {
401 if (StringUtils.contains(s, string)) {
402 return true;
403 }
404 }
405 return false;
406 }
407 }