All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.conqat.engine.commons.util.UnmodifiableCollectionsModule Maven / Gradle / Ivy

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