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

se.l4.commons.serialization.spi.SerializerResolverRegistry Maven / Gradle / Ivy

package se.l4.commons.serialization.spi;

import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;

import com.google.common.base.Throwables;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Sets;
import com.google.common.primitives.Primitives;

import se.l4.commons.serialization.SerializationException;
import se.l4.commons.serialization.Serializer;
import se.l4.commons.serialization.SerializerCollection;
import se.l4.commons.serialization.SerializerOrResolver;
import se.l4.commons.serialization.Use;
import se.l4.commons.serialization.collections.ArraySerializerResolver;
import se.l4.commons.types.InstanceFactory;

/**
 * Finder of {@link SerializerResolver}s, used when implementing a
 * {@link SerializerCollection}.
 *
 * @author Andreas Holstenson
 *
 */
public class SerializerResolverRegistry
{
	private static final SerializerResolver ARRAY_RESOLVER = new ArraySerializerResolver();

	private final InstanceFactory instanceFactory;
	private final NamingCallback naming;

	private final Map, SerializerResolver> boundTypeToResolver;
	private final LoadingCache, Optional>> typeToResolverCache;

	public SerializerResolverRegistry(InstanceFactory instanceFactory, NamingCallback naming)
	{
		this.instanceFactory = instanceFactory;
		this.naming = naming;

		boundTypeToResolver = new ConcurrentHashMap<>();
		typeToResolverCache = CacheBuilder.newBuilder()
			.build(new CacheLoader, Optional>>()
			{
				@Override
				public Optional> load(Class key)
					throws Exception
				{
					SerializerResolver result = findOrCreateSerializerResolver(key);
					return Optional.ofNullable(result);
				}
			});
	}

	/**
	 * Bind a resolver for the given type.
	 *
	 * @param type
	 * @param resolver
	 */
	public  void bind(Class type, SerializerResolver resolver)
	{
		typeToResolverCache.put(type, Optional.>of(resolver));
		boundTypeToResolver.put(type, resolver);
	}

	/**
	 * Get a resolver for the given type, returning {@code null} if
	 * the resolver can not be found.
	 *
	 * @param type
	 *   the {@link Class} to find a resolver for
	 * @return
	 *   the found resolver, or {@code null} if no resolver is found
	 * @throws SerializationException
	 *   if the resolver could not be constructed from some reason
	 */
	public SerializerResolver getResolver(Class type)
	{
		try
		{
			Optional> optional = typeToResolverCache.get(Primitives.wrap(type));

			return optional.isPresent() ? optional.get() : null;
		}
		catch (ExecutionException e)
		{
			Throwables.throwIfInstanceOf(e.getCause(), SerializationException.class);

			throw new SerializationException("Unable to retrieve serializer for " + type + "; " + e.getCause().getMessage(), e.getCause());
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected SerializerResolver findOrCreateSerializerResolver(Class from)
	{
		SerializerResolver resolver = createViaUse(from);
		if(resolver != null)
		{
			return resolver;
		}

		if(from.isArray())
		{
			// Arrays have special treatment, always use the array resolver
			return ARRAY_RESOLVER;
		}

		Set> resolvers = Sets.newLinkedHashSet();
		findSerializerResolver(from, resolvers);
		if(resolvers.isEmpty())
		{
			return null;
		}

		if(resolvers.size() == 1)
		{
			return resolvers.iterator().next();
		}

		return new SerializerResolverChain(resolvers);
	}

	protected void findSerializerResolver(Class type, Set> resolvers)
	{
		Class parent = type;
		while(parent != null)
		{
			SerializerResolver resolver = boundTypeToResolver.get(parent);
			if(resolver != null) resolvers.add(resolver);

			findSerializerResolverViaInterfaces(parent, resolvers);

			parent = parent.getSuperclass();
		}
	}

	protected void findSerializerResolverViaInterfaces(Class type, Set> resolvers)
	{
		Class[] interfaces = type.getInterfaces();
		for(Class intf : interfaces)
		{
			SerializerResolver resolver = boundTypeToResolver.get(intf);
			if(resolver != null) resolvers.add(resolver);
		}

		for(Class intf : interfaces)
		{
			findSerializerResolverViaInterfaces(intf, resolvers);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected SerializerResolver createViaUse(Class from)
	{
		if(from.isAnnotationPresent(Use.class))
		{
			// A specific serializer should be used
			Use annotation = from.getAnnotation(Use.class);
			final Class value = annotation.value();
			if(SerializerResolver.class.isAssignableFrom(value))
			{
				return (SerializerResolver) instanceFactory.create(value);
			}

			Serializer serializer = instanceFactory.create((Class) value);
			naming.registerIfNamed(from, serializer);
			return new StaticSerializerResolver(serializer);
		}

		return null;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy