org.conqat.engine.commons.util.UnmodifiableCollectionsModule Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of teamscale-commons Show documentation
Show all versions of teamscale-commons Show documentation
Provides common DTOs for Teamscale
The newest version!
package org.conqat.engine.commons.util;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.function.Function;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.conqat.lib.commons.collections.CollectionUtils;
import org.conqat.lib.commons.collections.UnmodifiableCollection;
import org.conqat.lib.commons.collections.UnmodifiableList;
import org.conqat.lib.commons.collections.UnmodifiableMap;
import org.conqat.lib.commons.collections.UnmodifiableSet;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
/**
* Jackson module which supports deserialization of {@link UnmodifiableCollection},
* {@link UnmodifiableList}, {@link UnmodifiableSet} and {@link UnmodifiableMap} objects from their
* JSON representation.
*/
public class UnmodifiableCollectionsModule extends SimpleModule {
private static final long serialVersionUID = 1L;
public UnmodifiableCollectionsModule() {
// Serialization is done via the default serializers for Collection/List/Set/Map
// We have to aid the java compiler with generic resolution by wrapping the
// creation into own methods.
addDeserializer(UnmodifiableCollection.class, createUnmodifiableCollectionDeserializer());
addDeserializer(UnmodifiableList.class, createUnmodifiableListDeserializer());
addDeserializer(UnmodifiableSet.class, createUnmodifiableSetDeserializer());
addDeserializer(UnmodifiableMap.class, UnmodifiableMapDeserializer.create());
}
@NonNull
private static UnmodifiableCollectionDeserializer> createUnmodifiableCollectionDeserializer() {
return UnmodifiableCollectionDeserializer.of(CollectionUtils::asUnmodifiable);
}
@NonNull
private static UnmodifiableCollectionDeserializer> createUnmodifiableListDeserializer() {
return UnmodifiableCollectionDeserializer
.of(collection -> CollectionUtils.asUnmodifiable(new ArrayList<>(collection)));
}
@NonNull
private static UnmodifiableCollectionDeserializer> createUnmodifiableSetDeserializer() {
return UnmodifiableCollectionDeserializer
// Use LinkedHashSet to keep ordering
.of(collection -> CollectionUtils.asUnmodifiable(new LinkedHashSet<>(collection)));
}
/**
* {@link JsonDeserializer} for {@link UnmodifiableCollection} and subclasses. Works by first
* deserializing the value into an intermediate {@link Collection} and then passing it to a
* {@link #unmodifiableTransformer}, which transforms it into the desired
* {@link UnmodifiableCollection}.
*
* @param
* collection element type
* @param
* {@link UnmodifiableCollection} target type
*/
private static class UnmodifiableCollectionDeserializer>
extends JsonDeserializer implements ContextualDeserializer {
/**
* {@link JavaType} of the contained collection element. Is initially {@code null} and filled when
* applied on a specific bean type with
* {@link #createContextual(DeserializationContext, BeanProperty)}.
*/
private final JavaType elementType;
/**
* Transforms the intermediate {@link Collection} into the target type {@code C}.
*/
private final Function, C> unmodifiableTransformer;
private UnmodifiableCollectionDeserializer(JavaType elementType,
Function, C> unmodifiableTransformer) {
this.elementType = elementType;
this.unmodifiableTransformer = unmodifiableTransformer;
}
/**
* Create a new {@link UnmodifiableCollectionDeserializer} by providing the needed
* {@code unmodifiableTransformer}.
*/
public static > UnmodifiableCollectionDeserializer of(
Function, C> unmodifiableTransformer) {
return new UnmodifiableCollectionDeserializer<>(null, unmodifiableTransformer);
}
@Override
public C deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
Collection intermediate = ctxt.readValue(p,
ctxt.getTypeFactory().constructCollectionLikeType(ArrayList.class, elementType));
return unmodifiableTransformer.apply(intermediate);
}
@Override
public JsonDeserializer> createContextual(DeserializationContext ctxt, BeanProperty property) {
JavaType propertyType = getPropertyType(ctxt, property);
return new UnmodifiableCollectionDeserializer<>(propertyType.containedType(0), unmodifiableTransformer);
}
private static JavaType getPropertyType(DeserializationContext ctxt, BeanProperty property) {
if (property == null) {
return ctxt.getContextualType();
}
return property.getType();
}
}
/**
* {@link JsonDeserializer} for {@link UnmodifiableMap}.
*
* @param
* map key type
* @param
* map value type
*/
private static class UnmodifiableMapDeserializer extends JsonDeserializer>
implements ContextualDeserializer {
/**
* {@link JavaType} of the contained Map key ({@code K}). Is initially {@code null} and filled when
* applied on a specific bean type with
* {@link #createContextual(DeserializationContext, BeanProperty)}.
*/
private final JavaType keyType;
/**
* {@link JavaType} of the contained Map value ({@code V}). Is initially {@code null} and filled
* when applied on a specific bean type with
* {@link #createContextual(DeserializationContext, BeanProperty)}.
*/
private final JavaType valueType;
private UnmodifiableMapDeserializer(JavaType keyType, JavaType valueType) {
this.keyType = keyType;
this.valueType = valueType;
}
/**
* Creates a new {@link UnmodifiableMapDeserializer}.
*/
public static UnmodifiableMapDeserializer create() {
return new UnmodifiableMapDeserializer<>(null, null);
}
@Override
public UnmodifiableMap deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
LinkedHashMap root = ctxt.readValue(parser,
// Use LinkedHashMap to keep the ordering
ctxt.getTypeFactory().constructMapType(LinkedHashMap.class, keyType, valueType));
return CollectionUtils.asUnmodifiable(root);
}
@Override
public JsonDeserializer> createContextual(DeserializationContext ctxt, BeanProperty property) {
JavaType mapType = UnmodifiableCollectionDeserializer.getPropertyType(ctxt, property);
return new UnmodifiableMapDeserializer<>(mapType.containedType(0), mapType.containedType(1));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy