From 204ff4979dca7e2cfcbe86cd1387bf6b6f2398c3 Mon Sep 17 00:00:00 2001 From: Antoine Aubry Date: Wed, 18 Jan 2017 11:47:25 +0000 Subject: [PATCH] Improve the (de)serializer builders so that it is possible to wrap existing component registrations. --- .../ValidatingDuringDeserialization.cs | 15 +-- YamlDotNet/Serialization/BuilderSkeleton.cs | 70 ++++++++++ .../Serialization/DeserializerBuilder.cs | 51 +++++++- .../IRegistrationLocationSelectionSyntax.cs | 8 ++ .../LazyComponentRegistrationList.cs | 122 ++++++++++++------ YamlDotNet/Serialization/SerializerBuilder.cs | 82 ++++++++++++ 6 files changed, 299 insertions(+), 49 deletions(-) diff --git a/YamlDotNet.Test/Samples/ValidatingDuringDeserialization.cs b/YamlDotNet.Test/Samples/ValidatingDuringDeserialization.cs index 671f34816..4d33402f5 100644 --- a/YamlDotNet.Test/Samples/ValidatingDuringDeserialization.cs +++ b/YamlDotNet.Test/Samples/ValidatingDuringDeserialization.cs @@ -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()) + .Build(); // This will fail with a validation exception var ex = Assert.Throws(() => diff --git a/YamlDotNet/Serialization/BuilderSkeleton.cs b/YamlDotNet/Serialization/BuilderSkeleton.cs index 3cb18684e..69210368e 100644 --- a/YamlDotNet/Serialization/BuilderSkeleton.cs +++ b/YamlDotNet/Serialization/BuilderSkeleton.cs @@ -140,6 +140,31 @@ Action> where return Self; } + /// + /// Registers an additional to be used by the (de)serializer. + /// + /// A factory that creates the based on a previously registered . + /// Configures the location where to insert the + public TBuilder WithTypeConverter( + WrapperFactory typeConverterFactory, + Action> 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; + } + /// /// Unregisters an existing of type . /// @@ -198,6 +223,31 @@ Action> where return Self; } + /// + /// Registers an additional to be used by the (de)serializer. + /// + /// A function that instantiates the type inspector based on a previously registered .. + /// Configures the location where to insert the + public TBuilder WithTypeInspector( + WrapperFactory typeInspectorFactory, + Action> 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; + } + /// /// Unregisters an existing of type . /// @@ -226,4 +276,24 @@ protected IEnumerable BuildTypeConverters() return typeConverterFactories.BuildComponentList(); } } + + /// + /// A factory that creates instances of based on an existing . + /// + /// The type of the wrapped component. + /// The type of the component that this factory creates. + /// The component that is to be wrapped. + /// Returns a new instance of that is based on . + public delegate TComponent WrapperFactory(TComponentBase wrapped) where TComponent : TComponentBase; + + /// + /// A factory that creates instances of based on an existing and an argument. + /// + /// The type of the argument. + /// The type of the wrapped component. + /// The type of the component that this factory creates. + /// The component that is to be wrapped. + /// The argument of the factory. + /// Returns a new instance of that is based on and . + public delegate TComponent WrapperFactory(TComponentBase wrapped, TArgument argument) where TComponent : TComponentBase; } \ No newline at end of file diff --git a/YamlDotNet/Serialization/DeserializerBuilder.cs b/YamlDotNet/Serialization/DeserializerBuilder.cs index bcf6a7180..2ff99c241 100644 --- a/YamlDotNet/Serialization/DeserializerBuilder.cs +++ b/YamlDotNet/Serialization/DeserializerBuilder.cs @@ -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; @@ -153,6 +152,31 @@ Action> where return this; } + /// + /// Registers an additional to be used by the deserializer. + /// + /// A factory that creates the based on a previously registered . + /// Configures the location where to insert the + public DeserializerBuilder WithNodeDeserializer( + WrapperFactory nodeDeserializerFactory, + Action> 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; + } + /// /// Unregisters an existing of type . /// @@ -208,6 +232,31 @@ Action> where return this; } + /// + /// Registers an additional to be used by the deserializer. + /// + /// A factory that creates the based on a previously registered . + /// Configures the location where to insert the + public DeserializerBuilder WithNodeTypeResolver( + WrapperFactory nodeTypeResolverFactory, + Action> 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; + } + /// /// Unregisters an existing of type . /// diff --git a/YamlDotNet/Serialization/IRegistrationLocationSelectionSyntax.cs b/YamlDotNet/Serialization/IRegistrationLocationSelectionSyntax.cs index daf397112..44d7f432c 100644 --- a/YamlDotNet/Serialization/IRegistrationLocationSelectionSyntax.cs +++ b/YamlDotNet/Serialization/IRegistrationLocationSelectionSyntax.cs @@ -48,4 +48,12 @@ public interface IRegistrationLocationSelectionSyntax /// void OnBottom(); } + + public interface ITrackingRegistrationLocationSelectionSyntax + { + /// + /// Registers the component in place of the already registered component of type . + /// + void InsteadOf() where TRegistrationType : TBaseRegistrationType; + } } \ No newline at end of file diff --git a/YamlDotNet/Serialization/LazyComponentRegistrationList.cs b/YamlDotNet/Serialization/LazyComponentRegistrationList.cs index 2907b27e0..36dbcdf51 100644 --- a/YamlDotNet/Serialization/LazyComponentRegistrationList.cs +++ b/YamlDotNet/Serialization/LazyComponentRegistrationList.cs @@ -52,6 +52,18 @@ public LazyComponentRegistration(Type componentType, Func } } + public sealed class TrackingLazyComponentRegistration + { + public readonly Type ComponentType; + public readonly Func Factory; + + public TrackingLazyComponentRegistration(Type componentType, Func factory) + { + ComponentType = componentType; + Factory = factory; + } + } + public void Add(Type componentType, Func factory) { entries.Add(new LazyComponentRegistration(componentType, factory)); @@ -95,6 +107,17 @@ Func factory ); } + public ITrackingRegistrationLocationSelectionSyntax CreateTrackingRegistrationLocationSelector( + Type componentType, + Func factory + ) + { + return new TrackingRegistrationLocationSelector( + this, + new TrackingLazyComponentRegistration(componentType, factory) + ); + } + public IEnumerator> GetEnumerator() { return entries.Select(e => e.Factory).GetEnumerator(); @@ -105,83 +128,110 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } - private class RegistrationLocationSelector : IRegistrationLocationSelectionSyntax + private int IndexOfRegistration(Type registrationType) { - private readonly LazyComponentRegistrationList registrations; - private readonly LazyComponentRegistration newRegistration; - - public RegistrationLocationSelector(LazyComponentRegistrationList 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() + { + 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() + private class RegistrationLocationSelector : IRegistrationLocationSelectionSyntax + { + private readonly LazyComponentRegistrationList registrations; + private readonly LazyComponentRegistration newRegistration; + + public RegistrationLocationSelector(LazyComponentRegistrationList 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.InsteadOf() { if (newRegistration.ComponentType != typeof(TRegistrationType)) { - EnsureNoDuplicateRegistrationType(); + registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); } - var registrationIndex = EnsureRegistrationExists(); + var registrationIndex = registrations.EnsureRegistrationExists(); registrations.entries[registrationIndex] = newRegistration; } void IRegistrationLocationSelectionSyntax.After() { - EnsureNoDuplicateRegistrationType(); - var registrationIndex = EnsureRegistrationExists(); + registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); + var registrationIndex = registrations.EnsureRegistrationExists(); registrations.entries.Insert(registrationIndex + 1, newRegistration); } void IRegistrationLocationSelectionSyntax.Before() { - EnsureNoDuplicateRegistrationType(); - var registrationIndex = EnsureRegistrationExists(); + registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); + var registrationIndex = registrations.EnsureRegistrationExists(); registrations.entries.Insert(registrationIndex, newRegistration); } void IRegistrationLocationSelectionSyntax.OnBottom() { - EnsureNoDuplicateRegistrationType(); + registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); registrations.entries.Add(newRegistration); } void IRegistrationLocationSelectionSyntax.OnTop() { - EnsureNoDuplicateRegistrationType(); + registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); registrations.entries.Insert(0, newRegistration); } } + + private class TrackingRegistrationLocationSelector : ITrackingRegistrationLocationSelectionSyntax + { + private readonly LazyComponentRegistrationList registrations; + private readonly TrackingLazyComponentRegistration newRegistration; + + public TrackingRegistrationLocationSelector(LazyComponentRegistrationList registrations, TrackingLazyComponentRegistration newRegistration) + { + this.registrations = registrations; + this.newRegistration = newRegistration; + } + + void ITrackingRegistrationLocationSelectionSyntax.InsteadOf() + { + if (newRegistration.ComponentType != typeof(TRegistrationType)) + { + registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); + } + + var registrationIndex = registrations.EnsureRegistrationExists(); + var innerComponentFactory = registrations.entries[registrationIndex].Factory; + registrations.entries[registrationIndex] = new LazyComponentRegistration( + newRegistration.ComponentType, + arg => newRegistration.Factory(innerComponentFactory(arg), arg) + ); + } + } } } \ No newline at end of file diff --git a/YamlDotNet/Serialization/SerializerBuilder.cs b/YamlDotNet/Serialization/SerializerBuilder.cs index bf4e7464c..d312d2dc5 100644 --- a/YamlDotNet/Serialization/SerializerBuilder.cs +++ b/YamlDotNet/Serialization/SerializerBuilder.cs @@ -112,6 +112,31 @@ Action> where return Self; } + /// + /// Registers an additional to be used by the serializer. + /// + /// A function that instantiates the event emitter based on a previously registered . + /// Configures the location where to insert the + public SerializerBuilder WithEventEmitter( + WrapperFactory eventEmitterFactory, + Action> where + ) + where TEventEmitter : IEventEmitter + { + if (eventEmitterFactory == null) + { + throw new ArgumentNullException("eventEmitterFactory"); + } + + if (where == null) + { + throw new ArgumentNullException("where"); + } + + where(eventEmitterFactories.CreateTrackingRegistrationLocationSelector(typeof(TEventEmitter), (wrapped, inner) => eventEmitterFactory(wrapped, inner))); + return Self; + } + /// /// Unregisters an existing of type . /// @@ -276,6 +301,37 @@ Action>> where return this; } + /// + /// Registers an additional to be used by the serializer + /// before emitting an object graph. + /// + /// + /// Registering a visitor in the pre-processing phase enables to traverse the object graph once + /// before actually emitting it. This allows a visitor to collect information about the graph that + /// can be used later by another visitor registered in the emission phase. + /// + /// A factory that creates the based on a previously registered . + /// Configures the location where to insert the + public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor( + WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory, + Action>> where + ) + where TObjectGraphVisitor : IObjectGraphVisitor + { + if (objectGraphVisitorFactory == null) + { + throw new ArgumentNullException("objectGraphVisitorFactory"); + } + + if (where == null) + { + throw new ArgumentNullException("where"); + } + + where(preProcessingPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (wrapped, _) => objectGraphVisitorFactory(wrapped))); + return this; + } + /// /// Unregisters an existing of type . /// @@ -336,6 +392,32 @@ Action>> wher return this; } + /// + /// Registers an additional to be used by the serializer + /// while emitting an object graph. + /// + /// A function that instantiates the type inspector based on a previously registered . + /// Configures the location where to insert the + public SerializerBuilder WithEmissionPhaseObjectGraphVisitor( + WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory, + Action>> where + ) + where TObjectGraphVisitor : IObjectGraphVisitor + { + if (objectGraphVisitorFactory == null) + { + throw new ArgumentNullException("objectGraphVisitorFactory"); + } + + if (where == null) + { + throw new ArgumentNullException("where"); + } + + where(emissionPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (wrapped, args) => objectGraphVisitorFactory(wrapped, args))); + return this; + } + /// /// Unregisters an existing of type . ///