Skip to content

Commit

Permalink
Improve the (de)serializer builders so that it is possible to wrap ex…
Browse files Browse the repository at this point in the history
…isting component registrations.
  • Loading branch information
aaubry committed Jan 18, 2017
1 parent 79a8e5d commit 204ff49
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 49 deletions.
15 changes: 3 additions & 12 deletions YamlDotNet.Test/Samples/ValidatingDuringDeserialization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,9 @@ public static void Main()
{
// Then we wrap the existing ObjectNodeDeserializer
// with our ValidatingNodeDeserializer:
var deserializer = new Deserializer();

var objectDeserializer = deserializer.NodeDeserializers
.Select((d, i) => new
{
Deserializer = d as ObjectNodeDeserializer,
Index = i
})
.First(d => d.Deserializer != null);

deserializer.NodeDeserializers[objectDeserializer.Index] =
new ValidatingNodeDeserializer(objectDeserializer.Deserializer);
var deserializer = new DeserializerBuilder()
.WithNodeDeserializer(inner => new ValidatingNodeDeserializer(inner), s => s.InsteadOf<ObjectNodeDeserializer>())
.Build();

// This will fail with a validation exception
var ex = Assert.Throws<YamlException>(() =>
Expand Down
70 changes: 70 additions & 0 deletions YamlDotNet/Serialization/BuilderSkeleton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,31 @@ Action<IRegistrationLocationSelectionSyntax<IYamlTypeConverter>> where
return Self;
}

/// <summary>
/// Registers an additional <see cref="IYamlTypeConverter" /> to be used by the (de)serializer.
/// </summary>
/// <param name="typeConverterFactory">A factory that creates the <see cref="IYamlTypeConverter" /> based on a previously registered <see cref="IYamlTypeConverter" />.</param>
/// <param name="where">Configures the location where to insert the <see cref="IYamlTypeConverter" /></param>
public TBuilder WithTypeConverter<TYamlTypeConverter>(
WrapperFactory<IYamlTypeConverter, IYamlTypeConverter> typeConverterFactory,
Action<ITrackingRegistrationLocationSelectionSyntax<IYamlTypeConverter>> where
)
where TYamlTypeConverter : IYamlTypeConverter
{
if (typeConverterFactory == null)
{
throw new ArgumentNullException("typeConverterFactory");
}

if (where == null)
{
throw new ArgumentNullException("where");
}

where(typeConverterFactories.CreateTrackingRegistrationLocationSelector(typeof(TYamlTypeConverter), (wrapped, _) => typeConverterFactory(wrapped)));
return Self;
}

/// <summary>
/// Unregisters an existing <see cref="IYamlTypeConverter" /> of type <typeparam name="TYamlTypeConverter" />.
/// </summary>
Expand Down Expand Up @@ -198,6 +223,31 @@ Action<IRegistrationLocationSelectionSyntax<ITypeInspector>> where
return Self;
}

/// <summary>
/// Registers an additional <see cref="ITypeInspector" /> to be used by the (de)serializer.
/// </summary>
/// <param name="typeInspectorFactory">A function that instantiates the type inspector based on a previously registered <see cref="ITypeInspector" />..</param>
/// <param name="where">Configures the location where to insert the <see cref="ITypeInspector" /></param>
public TBuilder WithTypeInspector<TTypeInspector>(
WrapperFactory<ITypeInspector, ITypeInspector, TTypeInspector> typeInspectorFactory,
Action<ITrackingRegistrationLocationSelectionSyntax<ITypeInspector>> where
)
where TTypeInspector : ITypeInspector
{
if (typeInspectorFactory == null)
{
throw new ArgumentNullException("typeInspectorFactory");
}

if (where == null)
{
throw new ArgumentNullException("where");
}

where(typeInspectorFactories.CreateTrackingRegistrationLocationSelector(typeof(TTypeInspector), (wrapped, inner) => typeInspectorFactory(wrapped, inner)));
return Self;
}

/// <summary>
/// Unregisters an existing <see cref="ITypeInspector" /> of type <typeparam name="TTypeInspector" />.
/// </summary>
Expand Down Expand Up @@ -226,4 +276,24 @@ protected IEnumerable<IYamlTypeConverter> BuildTypeConverters()
return typeConverterFactories.BuildComponentList();
}
}

/// <summary>
/// A factory that creates instances of <typeparamref name="TComponent" /> based on an existing <typeparamref name="TComponentBase" />.
/// </summary>
/// <typeparam name="TComponentBase">The type of the wrapped component.</typeparam>
/// <typeparam name="TComponent">The type of the component that this factory creates.</typeparam>
/// <param name="wrapped">The component that is to be wrapped.</param>
/// <returns>Returns a new instance of <typeparamref name="TComponent" /> that is based on <paramref name="wrapped" />.</returns>
public delegate TComponent WrapperFactory<TComponentBase, TComponent>(TComponentBase wrapped) where TComponent : TComponentBase;

/// <summary>
/// A factory that creates instances of <typeparamref name="TComponent" /> based on an existing <typeparamref name="TComponentBase" /> and an argument.
/// </summary>
/// <typeparam name="TArgument">The type of the argument.</typeparam>
/// <typeparam name="TComponentBase">The type of the wrapped component.</typeparam>
/// <typeparam name="TComponent">The type of the component that this factory creates.</typeparam>
/// <param name="wrapped">The component that is to be wrapped.</param>
/// <param name="argument">The argument of the factory.</param>
/// <returns>Returns a new instance of <typeparamref name="TComponent" /> that is based on <paramref name="wrapped" /> and <paramref name="argument" />.</returns>
public delegate TComponent WrapperFactory<TArgument, TComponentBase, TComponent>(TComponentBase wrapped, TArgument argument) where TComponent : TComponentBase;
}
51 changes: 50 additions & 1 deletion YamlDotNet/Serialization/DeserializerBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

using System;
using System.Collections.Generic;
using System.Linq;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
Expand Down Expand Up @@ -153,6 +152,31 @@ Action<IRegistrationLocationSelectionSyntax<INodeDeserializer>> where
return this;
}

/// <summary>
/// Registers an additional <see cref="INodeDeserializer" /> to be used by the deserializer.
/// </summary>
/// <param name="nodeDeserializerFactory">A factory that creates the <see cref="INodeDeserializer" /> based on a previously registered <see cref="INodeDeserializer" />.</param>
/// <param name="where">Configures the location where to insert the <see cref="INodeDeserializer" /></param>
public DeserializerBuilder WithNodeDeserializer<TNodeDeserializer>(
WrapperFactory<INodeDeserializer, TNodeDeserializer> nodeDeserializerFactory,
Action<ITrackingRegistrationLocationSelectionSyntax<INodeDeserializer>> where
)
where TNodeDeserializer : INodeDeserializer
{
if (nodeDeserializerFactory == null)
{
throw new ArgumentNullException("nodeDeserializerFactory");
}

if (where == null)
{
throw new ArgumentNullException("where");
}

where(nodeDeserializerFactories.CreateTrackingRegistrationLocationSelector(typeof(TNodeDeserializer), (wrapped, _) => nodeDeserializerFactory(wrapped)));
return this;
}

/// <summary>
/// Unregisters an existing <see cref="INodeDeserializer" /> of type <typeparam name="TNodeDeserializer" />.
/// </summary>
Expand Down Expand Up @@ -208,6 +232,31 @@ Action<IRegistrationLocationSelectionSyntax<INodeTypeResolver>> where
return this;
}

/// <summary>
/// Registers an additional <see cref="INodeTypeResolver" /> to be used by the deserializer.
/// </summary>
/// <param name="nodeTypeResolverFactory">A factory that creates the <see cref="INodeTypeResolver" /> based on a previously registered <see cref="INodeTypeResolver" />.</param>
/// <param name="where">Configures the location where to insert the <see cref="INodeTypeResolver" /></param>
public DeserializerBuilder WithNodeTypeResolver<TNodeTypeResolver>(
WrapperFactory<INodeTypeResolver, TNodeTypeResolver> nodeTypeResolverFactory,
Action<ITrackingRegistrationLocationSelectionSyntax<INodeTypeResolver>> where
)
where TNodeTypeResolver : INodeTypeResolver
{
if (nodeTypeResolverFactory == null)
{
throw new ArgumentNullException("nodeTypeResolverFactory");
}

if (where == null)
{
throw new ArgumentNullException("where");
}

where(nodeTypeResolverFactories.CreateTrackingRegistrationLocationSelector(typeof(TNodeTypeResolver), (wrapped, _) => nodeTypeResolverFactory(wrapped)));
return this;
}

/// <summary>
/// Unregisters an existing <see cref="INodeTypeResolver" /> of type <typeparam name="TNodeTypeResolver" />.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,12 @@ public interface IRegistrationLocationSelectionSyntax<TBaseRegistrationType>
/// </summary>
void OnBottom();
}

public interface ITrackingRegistrationLocationSelectionSyntax<TBaseRegistrationType>
{
/// <summary>
/// Registers the component in place of the already registered component of type <typeparamref name="TRegistrationType" />.
/// </summary>
void InsteadOf<TRegistrationType>() where TRegistrationType : TBaseRegistrationType;
}
}
122 changes: 86 additions & 36 deletions YamlDotNet/Serialization/LazyComponentRegistrationList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ public LazyComponentRegistration(Type componentType, Func<TArgument, TComponent>
}
}

public sealed class TrackingLazyComponentRegistration
{
public readonly Type ComponentType;
public readonly Func<TComponent, TArgument, TComponent> Factory;

public TrackingLazyComponentRegistration(Type componentType, Func<TComponent, TArgument, TComponent> factory)
{
ComponentType = componentType;
Factory = factory;
}
}

public void Add(Type componentType, Func<TArgument, TComponent> factory)
{
entries.Add(new LazyComponentRegistration(componentType, factory));
Expand Down Expand Up @@ -95,6 +107,17 @@ Func<TArgument, TComponent> factory
);
}

public ITrackingRegistrationLocationSelectionSyntax<TComponent> CreateTrackingRegistrationLocationSelector(
Type componentType,
Func<TComponent, TArgument, TComponent> factory
)
{
return new TrackingRegistrationLocationSelector(
this,
new TrackingLazyComponentRegistration(componentType, factory)
);
}

public IEnumerator<Func<TArgument, TComponent>> GetEnumerator()
{
return entries.Select(e => e.Factory).GetEnumerator();
Expand All @@ -105,83 +128,110 @@ IEnumerator IEnumerable.GetEnumerator()
return GetEnumerator();
}

private class RegistrationLocationSelector : IRegistrationLocationSelectionSyntax<TComponent>
private int IndexOfRegistration(Type registrationType)
{
private readonly LazyComponentRegistrationList<TArgument, TComponent> registrations;
private readonly LazyComponentRegistration newRegistration;

public RegistrationLocationSelector(LazyComponentRegistrationList<TArgument, TComponent> registrations, LazyComponentRegistration newRegistration)
for (int i = 0; i < entries.Count; ++i)
{
this.registrations = registrations;
this.newRegistration = newRegistration;
if (registrationType == entries[i].ComponentType)
{
return i;
}
}
return -1;
}

private int IndexOfRegistration(Type registrationType)
private void EnsureNoDuplicateRegistrationType(Type componentType)
{
if (IndexOfRegistration(componentType) != -1)
{
for (int i = 0; i < registrations.entries.Count; ++i)
{
if (registrationType == registrations.entries[i].ComponentType)
{
return i;
}
}
return -1;
throw new InvalidOperationException(string.Format("A component of type '{0}' has already been registered.", componentType.FullName));
}
}

private void EnsureNoDuplicateRegistrationType()
private int EnsureRegistrationExists<TRegistrationType>()
{
var registrationIndex = IndexOfRegistration(typeof(TRegistrationType));
if (registrationIndex == -1)
{
if (IndexOfRegistration(newRegistration.ComponentType) != -1)
{
throw new InvalidOperationException(string.Format("A component of type '{0}' has already been registered.", newRegistration.ComponentType.FullName));
}
throw new InvalidOperationException(string.Format("A component of type '{0}' has not been registered.", typeof(TRegistrationType).FullName));
}
return registrationIndex;
}

private int EnsureRegistrationExists<TRegistrationType>()
private class RegistrationLocationSelector : IRegistrationLocationSelectionSyntax<TComponent>
{
private readonly LazyComponentRegistrationList<TArgument, TComponent> registrations;
private readonly LazyComponentRegistration newRegistration;

public RegistrationLocationSelector(LazyComponentRegistrationList<TArgument, TComponent> registrations, LazyComponentRegistration newRegistration)
{
var registrationIndex = IndexOfRegistration(typeof(TRegistrationType));
if (registrationIndex == -1)
{
throw new InvalidOperationException(string.Format("A component of type '{0}' has not been registered.", typeof(TRegistrationType).FullName));
}
return registrationIndex;
this.registrations = registrations;
this.newRegistration = newRegistration;
}

void IRegistrationLocationSelectionSyntax<TComponent>.InsteadOf<TRegistrationType>()
{
if (newRegistration.ComponentType != typeof(TRegistrationType))
{
EnsureNoDuplicateRegistrationType();
registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType);
}

var registrationIndex = EnsureRegistrationExists<TRegistrationType>();
var registrationIndex = registrations.EnsureRegistrationExists<TRegistrationType>();
registrations.entries[registrationIndex] = newRegistration;
}

void IRegistrationLocationSelectionSyntax<TComponent>.After<TRegistrationType>()
{
EnsureNoDuplicateRegistrationType();
var registrationIndex = EnsureRegistrationExists<TRegistrationType>();
registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType);
var registrationIndex = registrations.EnsureRegistrationExists<TRegistrationType>();
registrations.entries.Insert(registrationIndex + 1, newRegistration);
}

void IRegistrationLocationSelectionSyntax<TComponent>.Before<TRegistrationType>()
{
EnsureNoDuplicateRegistrationType();
var registrationIndex = EnsureRegistrationExists<TRegistrationType>();
registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType);
var registrationIndex = registrations.EnsureRegistrationExists<TRegistrationType>();
registrations.entries.Insert(registrationIndex, newRegistration);
}

void IRegistrationLocationSelectionSyntax<TComponent>.OnBottom()
{
EnsureNoDuplicateRegistrationType();
registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType);
registrations.entries.Add(newRegistration);
}

void IRegistrationLocationSelectionSyntax<TComponent>.OnTop()
{
EnsureNoDuplicateRegistrationType();
registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType);
registrations.entries.Insert(0, newRegistration);
}
}

private class TrackingRegistrationLocationSelector : ITrackingRegistrationLocationSelectionSyntax<TComponent>
{
private readonly LazyComponentRegistrationList<TArgument, TComponent> registrations;
private readonly TrackingLazyComponentRegistration newRegistration;

public TrackingRegistrationLocationSelector(LazyComponentRegistrationList<TArgument, TComponent> registrations, TrackingLazyComponentRegistration newRegistration)
{
this.registrations = registrations;
this.newRegistration = newRegistration;
}

void ITrackingRegistrationLocationSelectionSyntax<TComponent>.InsteadOf<TRegistrationType>()
{
if (newRegistration.ComponentType != typeof(TRegistrationType))
{
registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType);
}

var registrationIndex = registrations.EnsureRegistrationExists<TRegistrationType>();
var innerComponentFactory = registrations.entries[registrationIndex].Factory;
registrations.entries[registrationIndex] = new LazyComponentRegistration(
newRegistration.ComponentType,
arg => newRegistration.Factory(innerComponentFactory(arg), arg)
);
}
}
}
}
Loading

0 comments on commit 204ff49

Please sign in to comment.