MorphToCollection.java
package org.klojang.convert;
import org.klojang.util.CollectionMethods;
import org.klojang.util.InvokeMethods;
import java.lang.reflect.Array;
import java.util.*;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Supplier;
@SuppressWarnings({"rawtypes", "unchecked"})
final class MorphToCollection {
private MorphToCollection() {
throw new UnsupportedOperationException();
}
private static final Map<Class, Function<Object, Collection>> table = Map.of(
Iterable.class, MorphToCollection::toList,
Collection.class, MorphToCollection::toList,
List.class, obj -> toCollection1(obj, ArrayList::new),
ArrayList.class, obj -> toCollection1(obj, ArrayList::new),
LinkedList.class, obj -> toCollection2(obj, LinkedList::new),
Set.class, obj -> toCollection1(obj, HashSet::new),
HashSet.class, obj -> toCollection1(obj, HashSet::new),
LinkedHashSet.class, obj -> toCollection2(obj, LinkedHashSet::new),
SortedSet.class, obj -> toCollection2(obj, TreeSet::new),
TreeSet.class, obj -> toCollection2(obj, TreeSet::new));
static <T extends Collection> T morph(Object obj, Class toType) {
Function<Object, Collection> converter = table.get(toType);
if (converter == null) {
return toSpecialCollection(obj, (Class<T>) toType);
}
return (T) converter.apply(obj);
}
private static <T extends Collection> T toSpecialCollection(Object obj,
Class<T> toType) {
T collection;
try {
// If toType has a no-arg constructor, we're good. Otherwise we give up.
collection = InvokeMethods.newInstance(toType);
} catch (Throwable t) {
throw new TypeConversionException(obj, toType, t.toString());
}
return (T) toCollection2(obj, () -> collection);
}
private static Collection toList(Object obj) {
Collection collection;
if (obj.getClass().isArray()) {
collection = new ArrayList(Array.getLength(obj));
copyArrayElements(obj, collection);
} else {
collection = Collections.singletonList(obj);
}
return collection;
}
private static Collection toCollection1(Object obj,
IntFunction<Collection> constructor) {
Collection collection;
if (obj instanceof Collection src) {
collection = constructor.apply(src.size());
collection.addAll(src);
} else if (obj.getClass().isArray()) {
collection = constructor.apply(Array.getLength(obj));
copyArrayElements(obj, collection);
} else {
collection = constructor.apply(1);
collection.add(obj);
}
return collection;
}
private static Collection toCollection2(Object obj,
Supplier<Collection> supplier) {
Collection collection = supplier.get();
if (obj instanceof Collection) {
collection = supplier.get();
collection.addAll((Collection) obj);
} else if (obj.getClass().isArray()) {
copyArrayElements(obj, collection);
} else {
collection.add(obj);
}
return collection;
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static void copyArrayElements(Object fromArray,
Collection<?> toCollection) {
if (fromArray instanceof Object[] objs) {
((Collection) toCollection).addAll(Arrays.asList(objs));
} else {
((Collection) toCollection).addAll(CollectionMethods.asList(fromArray));
}
}
}