java-commons/commons-serialize-databind/src/main/java/io/gitlab/jfronny/commons/serialize/databind/impl/adapter/CollectionTypeAdapterFactor...

90 lines
4.2 KiB
Java

package io.gitlab.jfronny.commons.serialize.databind.impl.adapter;
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import io.gitlab.jfronny.commons.serialize.Token;
import io.gitlab.jfronny.commons.serialize.databind.ObjectMapper;
import io.gitlab.jfronny.commons.serialize.databind.TypeAdapterFactory;
import io.gitlab.jfronny.commons.serialize.databind.api.TypeAdapter;
import io.gitlab.jfronny.commons.serialize.databind.api.TypeToken;
import io.gitlab.jfronny.commons.serialize.databind.api.TypeUtils;
import java.lang.reflect.Type;
import java.util.*;
public class CollectionTypeAdapterFactory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(ObjectMapper mapper, TypeToken<T> type) {
Class<? super T> rawType = type.getRawType();
if (!Collection.class.isAssignableFrom(rawType)) {
return null;
}
Type elementType = TypeUtils.getCollectionElementType(type.getType(), rawType);
TypeAdapter<?> elementTypeAdapter = mapper.getAdapter(TypeToken.get(elementType));
@SuppressWarnings({"unchecked", "rawtypes"}) // create() doesn't define a type parameter
TypeAdapter<T> result = new Adapter(mapper, elementType, elementTypeAdapter, rawType);
return result;
}
private static final class Adapter<E> extends TypeAdapter<Collection<E>> {
private static final Set<Class<?>> RAW_TYPES_LIST = Set.of(List.class, AbstractList.class, SequencedCollection.class, Collection.class, AbstractCollection.class, Iterable.class);
private static final Set<Class<?>> RAW_TYPES_LINKED = Set.of(Deque.class, Queue.class, AbstractSequentialList.class);
private static final Set<Class<?>> RAW_TYPES_SET = Set.of(Set.class, SequencedSet.class, AbstractSet.class);
private static final Set<Class<?>> RAW_TYPES_SSET = Set.of(SortedSet.class, NavigableSet.class);
private final TypeAdapter<E> elementTypeAdapter;
private final Class<?> implClass;
public Adapter(
ObjectMapper context,
Type elementType,
TypeAdapter<E> elementTypeAdapter, Class<?> implClass) {
this.elementTypeAdapter = new TypeAdapterRuntimeTypeWrapper<>(context, elementTypeAdapter, elementType);
this.implClass = RAW_TYPES_LIST.contains(implClass) ? ArrayList.class
: RAW_TYPES_LINKED.contains(implClass) ? LinkedList.class
: RAW_TYPES_SET.contains(implClass) ? LinkedHashSet.class
: RAW_TYPES_SSET.contains(implClass) ? TreeSet.class
: implClass;
}
@Override
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void serialize(Collection<E> value, Writer writer) throws TEx, MalformedDataException {
if (value == null) {
writer.nullValue();
return;
}
writer.beginArray();
for (E element : value) {
elementTypeAdapter.serialize(element, writer);
}
writer.endArray();
}
@Override
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Collection<E> deserialize(Reader reader) throws TEx, MalformedDataException {
if (reader.peek() == Token.NULL) {
reader.nextNull();
return null;
}
Collection<E> collection = (Collection<E>) TypeUtils.instantiate(implClass).orElseThrow(() -> new MalformedDataException("Cannot instantiate collection of type " + implClass.getName()));
if (!reader.isLenient() || reader.peek() == Token.BEGIN_ARRAY) {
reader.beginArray();
while (reader.hasNext()) {
E instance = elementTypeAdapter.deserialize(reader);
collection.add(instance);
}
reader.endArray();
} else {
// Coerce
collection.add(elementTypeAdapter.deserialize(reader));
}
return collection;
}
}
}