blob: d04e61842d2f4357971fc081bc0155efb4422db5 [file] [log] [blame]
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
// * with the License. You may obtain a copy of the License at *
// * *
// * http://www.apache.org/licenses/LICENSE-2.0 *
// * *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
package org.apache.juneau.internal;
import static org.apache.juneau.internal.StringUtils.*;
import static org.apache.juneau.internal.ObjectUtils.*;
import java.lang.reflect.*;
import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.parser.*;
/**
* Utility methods for collections.
*/
public final class CollectionUtils {
/**
* Add a value to a list if the value is not null.
*
* @param l The list to add to.
* @param o The element to add.
* @return The same list.
*/
public static <T> List<T> addIfNotNull(List<T> l, T o) {
if (o != null)
l.add(o);
return l;
}
/**
* Adds the contents of one list to the other in reverse order.
*
* <p>
* i.e. add values from 2nd list from end-to-start order to the end of the 1st list.
*
* @param list The list to append to.
* @param append Contains the values to append to the list.
* @return The same list.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static List<?> addReverse(List list, List append) {
for (ListIterator i = append.listIterator(append.size()); i.hasPrevious();)
list.add(i.previous());
return list;
}
/**
* Adds the contents of the array to the list in reverse order.
*
* <p>
* i.e. add values from the array from end-to-start order to the end of the list.
*
* @param list The list to append to.
* @param append Contains the values to append to the list.
* @return The same list.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static List<?> addReverse(List list, Object[] append) {
for (int i = append.length - 1; i >= 0; i--)
list.add(append[i]);
return list;
}
/**
* Returns an iterable over the specified collection.
*
* @param c The collection to iterate over.
* @param reverse If <jk>true</jk>, iterate in reverse order.
* @return An iterable over the collection.
*/
public static <T> Iterable<T> iterable(final Collection<T> c, boolean reverse) {
if (reverse)
return new ReverseIterable<>(c instanceof List ? (List<T>)c : new ArrayList<>(c));
return c;
}
/**
* Returns an iterable over the specified list.
*
* @param c The collection to iterate over.
* @param reverse If <jk>true</jk>, iterate in reverse order.
* @return An iterable over the collection.
*/
public static <T> Iterable<T> iterable(final List<T> c, boolean reverse) {
if (reverse)
return new ReverseIterable<>(c);
return c;
}
/**
* Returns a reverse iterable over the specified list.
*
* @param c The collection to iterate over.
* @return An iterable over the collection.
*/
public static <T> Iterable<T> reverseIterable(final List<T> c) {
return iterable(c, true);
}
/**
* Returns an iterable over the specified array.
*
* @param c The collection to iterate over.
* @param reverse If <jk>true</jk>, iterate in reverse order.
* @return An iterable over the collection.
*/
public static <T> Iterable<T> iterable(T[] c, boolean reverse) {
if (reverse)
return new ReverseIterable<>(Arrays.asList(c));
return Arrays.asList(c);
}
/**
* Returns a reverse iterable over the specified array.
*
* @param c The collection to iterate over.
* @return An iterable over the collection.
*/
public static <T> Iterable<T> reverseIterable(T[] c) {
return iterable(c, true);
}
/**
* Returns an iterable over the specified enumeration.
*
* @param e The collection to iterate over.
* @return An iterable over the enumeration.
*/
public static <E> Iterable<E> iterable(final Enumeration<E> e) {
if (e == null)
return null;
return new Iterable<E>() {
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
@Override
public boolean hasNext() {
return e.hasMoreElements();
}
@Override
public E next() {
return e.nextElement();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
}
/**
* Creates an unmodifiable list from the specified list.
*
* @param l The collection to copy from.
* @return An unmodifiable view of the list, or a {@link Collections#emptyList()}
* if the list was empty or <jk>null</jk>.
*/
public static <T> List<T> unmodifiableList(List<T> l) {
if (l == null || l.isEmpty())
return Collections.emptyList();
return Collections.unmodifiableList(l);
}
/**
* Creates an unmodifiable list from the specified array.
*
* @param l The collection to copy from.
* @return An unmodifiable view of the list, or a {@link Collections#emptyList()}
* if the list was empty or <jk>null</jk>.
*/
public static <T> List<T> unmodifiableList(T[] l) {
if (l == null || l.length == 0)
return Collections.emptyList();
return Collections.unmodifiableList(Arrays.asList(l));
}
/**
* Creates an immutable list from the specified collection.
*
* @param l The collection to copy from.
* @return An unmodifiable {@link ArrayList} copy of the collection, or a {@link Collections#emptyList()}
* if the collection was empty or <jk>null</jk>.
*/
public static <T> List<T> immutableList(Collection<T> l) {
if (l == null || l.isEmpty())
return Collections.emptyList();
return Collections.unmodifiableList(new ArrayList<>(l));
}
/**
* Creates an immutable list from the specified array.
*
* @param l The array to copy from.
* @return An unmodifiable {@link ArrayList} copy of the collection, or a {@link Collections#emptyList()}
* if the collection was empty or <jk>null</jk>.
*/
public static <T> List<T> immutableList(T[] l) {
if (l == null || l.length == 0)
return Collections.emptyList();
return Collections.unmodifiableList(new ArrayList<>(Arrays.asList(l)));
}
/**
* Creates an immutable map from the specified map.
*
* @param m The map to copy from.
* @return An unmodifiable {@link LinkedHashMap} copy of the collection, or a {@link Collections#emptyMap()}
* if the collection was empty or <jk>null</jk>.
*/
public static <K,V> Map<K,V> immutableMap(Map<K,V> m) {
if (m == null || m.isEmpty())
return Collections.emptyMap();
return Collections.unmodifiableMap(new LinkedHashMap<>(m));
}
/**
* Creates an unmodifiable map from the specified map.
*
* @param m The map to copy from.
* @return An unmodifiable view of the collection, or a {@link Collections#emptyMap()}
* if the collection was empty or <jk>null</jk>.
*/
public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> m) {
if (m == null || m.isEmpty())
return Collections.emptyMap();
return Collections.unmodifiableMap(m);
}
/**
* Adds a set of values to an existing list.
*
* @param appendTo
* The list to append to.
* <br>If <jk>null</jk>, a new {@link ArrayList} will be created.
* @param values The values to add.
* @param type The data type of the elements.
* @param args The generic type arguments of the data type.
* @return The converted value, or <jk>null</jk> if the input was null.
*/
public static <T> List<T> addToList(List<T> appendTo, Object[] values, Class<T> type, Type...args) {
if (values == null)
return appendTo;
try {
List<T> l = appendTo;
if (appendTo == null)
l = new ArrayList<>();
for (Object o : values) {
if (o != null) {
if (isObjectList(o, false)) {
for (Object o2 : new ObjectList(o.toString()))
l.add(toType(o2, type, args));
} else if (o instanceof Collection) {
for (Object o2 : (Collection<?>)o)
l.add(toType(o2, type, args));
} else if (o.getClass().isArray()) {
for (int i = 0; i < Array.getLength(o); i++)
l.add(toType(Array.get(o, i), type, args));
} else {
l.add(toType(o, type, args));
}
}
}
return l.isEmpty() ? null : l;
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
/**
* Adds a set of values to an existing map.
*
* @param appendTo
* The map to append to.
* <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created.
* @param values The values to add.
* @param keyType The data type of the keys.
* @param valueType The data type of the values.
* @param valueTypeArgs The generic type arguments of the data type of the values.
* @return The converted value, or <jk>null</jk> if the input was null.
*/
@SuppressWarnings("unchecked")
public static <K,V> Map<K,V> addToMap(Map<K,V> appendTo, Object[] values, Class<K> keyType, Class<V> valueType, Type...valueTypeArgs) {
if (values == null)
return appendTo;
try {
Map<K,V> m = appendTo;
if (m == null)
m = new LinkedHashMap<>();
for (Object o : values) {
if (o != null) {
if (isObjectMap(o, false)) {
for (Map.Entry<String,Object> e : new ObjectMap(o.toString()).entrySet())
m.put(toType(e.getKey(), keyType), toType(e.getValue(), valueType, valueTypeArgs));
} else if (o instanceof Map) {
for (Map.Entry<Object,Object> e : ((Map<Object,Object>)o).entrySet())
m.put(toType(e.getKey(), keyType), toType(e.getValue(), valueType, valueTypeArgs));
} else {
throw new FormattedRuntimeException("Invalid object type {0} passed to addToMap()", o.getClass().getName());
}
}
}
return m.isEmpty() ? null : m;
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
/**
* Creates a new list from the specified collection.
*
* @param val The value to copy from.
* @return A new {@link ArrayList}, or <jk>null</jk> if the input was null.
*/
public static <T> List<T> newList(Collection<T> val) {
if (val == null)
return null;
return new ArrayList<>(val);
}
/**
* Copies the specified values into an existing list.
*
* @param l
* The list to add to.
* <br>If <jk>null</jk>, a new {@link ArrayList} will be created.
* @param val The values to add.
* @return The list with values copied into it.
*/
public static <T> List<T> addToList(List<T> l, Collection<T> val) {
if (val != null) {
if (l == null)
l = new ArrayList<>(val);
else
l.addAll(val);
}
return l;
}
/**
* Creates a new map from the specified map.
*
* @param val The value to copy from.
* @return A new {@link LinkedHashMap}, or <jk>null</jk> if the input was null.
*/
public static <K,V> Map<K,V> newMap(Map<K,V> val) {
if (val == null)
return null;
return new LinkedHashMap<>(val);
}
/**
* Copies the specified values into an existing map.
*
* @param m
* The map to add to.
* <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created.
* @param val The values to add.
* @return The list with values copied into it.
*/
public static <K,V> Map<K,V> addToMap(Map<K,V> m, Map<K,V> val) {
if (val != null) {
if (m == null)
m = new LinkedHashMap<>(val);
else
m.putAll(val);
}
return m;
}
/**
* Adds a single entry into an existing map.
*
* @param m
* The map to add to.
* <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created.
* @param key The entry key.
* @param value The entry value.
* @return The list with values copied into it.
*/
public static <K,V> Map<K,V> addToMap(Map<K,V> m, K key, V value) {
if (m == null)
m = new LinkedHashMap<>();
m.put(key, value);
return m;
}
/**
* Creates a new map from the specified map.
*
* @param val The value to copy from.
* @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering.
* @return A new {@link LinkedHashMap}, or <jk>null</jk> if the input was null.
*/
public static <K,V> Map<K,V> newSortedMap(Map<K,V> val, Comparator<K> comparator) {
if (val == null)
return null;
Map<K,V> m = new TreeMap<>(comparator);
m.putAll(val);
return m;
}
/**
* Creates a case-insensitive ordered set out of the specified string values.
*
* @param values The values to populate the set with.
* @return A new ordered set.
*/
public static Set<String> newSortedCaseInsensitiveSet(String...values) {
Set<String> s = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER) {
private static final long serialVersionUID = 1L;
@Override
public boolean contains(Object v) {
return v == null ? false : super.contains(v);
}
};
for (String v : values)
if (v != null)
s.add(v);
return s;
}
/**
* Creates a case-insensitive ordered set out of the specified string values.
*
* @param values
* A comma-delimited list of the values to populate the set with.
* @return A new ordered set.
*/
public static Set<String> newSortedCaseInsensitiveSet(String values) {
return newSortedCaseInsensitiveSet(StringUtils.split(StringUtils.emptyIfNull(values)));
}
/**
* Same as {@link #newSortedCaseInsensitiveSet(String)} but makes the set unmodifiable.
*
* @param values
* A comma-delimited list of the values to populate the set with.
* @return A new ordered set.
*/
public static Set<String> newUnmodifiableSortedCaseInsensitiveSet(String values) {
return Collections.unmodifiableSet(newSortedCaseInsensitiveSet(StringUtils.split(StringUtils.emptyIfNull(values))));
}
/**
* Copies the specified values into an existing map.
*
* @param m
* The map to add to.
* <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created.
* @param val The values to add.
* @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering.
* @return The list with values copied into it.
*/
public static <K,V> Map<K,V> addToSortedMap(Map<K,V> m, Map<K,V> val, Comparator<K> comparator) {
if (val != null) {
if (m == null) {
m = new TreeMap<>(comparator);
m.putAll(val);
} else {
m.putAll(val);
}
}
return m;
}
/**
* Adds a single entry into an existing map.
*
* @param m
* The map to add to.
* <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created.
* @param key The entry key.
* @param value The entry value.
* @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering.
* @return The list with values copied into it.
*/
public static <K,V> Map<K,V> addToSortedMap(Map<K,V> m, K key, V value, Comparator<K> comparator) {
if (m == null)
m = new TreeMap<>(comparator);
m.put(key, value);
return m;
}
/**
* Converts the specified arguments into an unmodifiable {@link HashSet}.
*
* @param values The entries to populate the hashset with.
* @return A new {@link HashSet} populated with the specified arguments.
*/
@SafeVarargs
public static <T> Set<T> newUnmodifiableHashSet(T...values) {
return Collections.unmodifiableSet(newHashSet(values));
}
/**
* Converts the specified arguments into a modifiable {@link HashSet}.
*
* @param values The entries to populate the hashset with.
* @return A new {@link HashSet} populated with the specified arguments.
*/
@SafeVarargs
public static <T> Set<T> newHashSet(T...values) {
return new HashSet<>(Arrays.asList(values));
}
}