// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using MessagePack.Formatters;
using MessagePack.Internal;
namespace MessagePack.Resolvers
{
///
/// Represents a collection of formatters and resolvers acting as one.
///
///
/// This class is not thread-safe for mutations. It is thread-safe when not being written to.
///
public static class CompositeResolver
{
private static readonly ReadOnlyDictionary EmptyFormattersByType = new ReadOnlyDictionary(new Dictionary());
///
/// Initializes a new instance of an with the specified formatters and sub-resolvers.
///
///
/// A list of instances of to prefer (above the ).
/// The formatters are searched in the order given, so if two formatters support serializing the same type, the first one is used.
/// May not be null, but may be .
///
///
/// A list of resolvers to use for serializing types for which does not include a formatter.
/// The resolvers are searched in the order given, so if two resolvers support serializing the same type, the first one is used.
/// May not be null, but may be .
///
///
/// An instance of .
///
public static IFormatterResolver Create(IReadOnlyList formatters, IReadOnlyList resolvers)
{
if (formatters is null)
{
throw new ArgumentNullException(nameof(formatters));
}
if (resolvers is null)
{
throw new ArgumentNullException(nameof(resolvers));
}
// Make a copy of the resolvers list provided by the caller to guard against them changing it later.
var immutableFormatters = formatters.ToArray();
var immutableResolvers = resolvers.ToArray();
return new CachingResolver(immutableFormatters, immutableResolvers);
}
public static IFormatterResolver Create(params IFormatterResolver[] resolvers) => Create(Array.Empty(), resolvers);
public static IFormatterResolver Create(params IMessagePackFormatter[] formatters) => Create(formatters, Array.Empty());
private class CachingResolver : IFormatterResolver
{
private readonly ThreadsafeTypeKeyHashTable formattersCache = new ThreadsafeTypeKeyHashTable();
private readonly IMessagePackFormatter[] subFormatters;
private readonly IFormatterResolver[] subResolvers;
///
/// Initializes a new instance of the class.
///
internal CachingResolver(IMessagePackFormatter[] subFormatters, IFormatterResolver[] subResolvers)
{
this.subFormatters = subFormatters ?? throw new ArgumentNullException(nameof(subFormatters));
this.subResolvers = subResolvers ?? throw new ArgumentNullException(nameof(subResolvers));
}
public IMessagePackFormatter GetFormatter()
{
if (!this.formattersCache.TryGetValue(typeof(T), out IMessagePackFormatter formatter))
{
foreach (var subFormatter in this.subFormatters)
{
if (subFormatter is IMessagePackFormatter)
{
formatter = subFormatter;
goto CACHE;
}
}
foreach (IFormatterResolver resolver in this.subResolvers)
{
formatter = resolver.GetFormatter();
if (formatter != null)
{
goto CACHE;
}
}
// when not found, cache null.
CACHE:
this.formattersCache.TryAdd(typeof(T), formatter);
}
return (IMessagePackFormatter)formatter;
}
}
}
}