// This file is part of YamlDotNet - A .NET library for YAML. // Copyright (c) Antoine Aubry and contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace YamlDotNet.Serialization { internal sealed class LazyComponentRegistrationList : IEnumerable> { private readonly List entries = new List(); public LazyComponentRegistrationList Clone() { var clone = new LazyComponentRegistrationList(); foreach (var entry in entries) { clone.entries.Add(entry); } return clone; } public sealed class LazyComponentRegistration { public readonly Type ComponentType; public readonly Func Factory; public LazyComponentRegistration(Type componentType, Func factory) { ComponentType = componentType; Factory = factory; } } 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)); } public void Remove(Type componentType) { for (var i = 0; i < entries.Count; ++i) { if (entries[i].ComponentType == componentType) { entries.RemoveAt(i); return; } } throw new KeyNotFoundException($"A component registration of type '{componentType.FullName}' was not found."); } public int Count => entries.Count; public IEnumerable> InReverseOrder { get { for (var i = entries.Count - 1; i >= 0; --i) { yield return entries[i].Factory; } } } public IRegistrationLocationSelectionSyntax CreateRegistrationLocationSelector( Type componentType, Func factory ) { return new RegistrationLocationSelector( this, new LazyComponentRegistration(componentType, 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(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private int IndexOfRegistration(Type registrationType) { for (var i = 0; i < entries.Count; ++i) { if (registrationType == entries[i].ComponentType) { return i; } } return -1; } private void EnsureNoDuplicateRegistrationType(Type componentType) { if (IndexOfRegistration(componentType) != -1) { throw new InvalidOperationException($"A component of type '{componentType.FullName}' has already been registered."); } } private int EnsureRegistrationExists() { var registrationIndex = IndexOfRegistration(typeof(TRegistrationType)); if (registrationIndex == -1) { throw new InvalidOperationException($"A component of type '{typeof(TRegistrationType).FullName}' has not been registered."); } return registrationIndex; } private class RegistrationLocationSelector : IRegistrationLocationSelectionSyntax { private readonly LazyComponentRegistrationList registrations; private readonly LazyComponentRegistration newRegistration; public RegistrationLocationSelector(LazyComponentRegistrationList registrations, LazyComponentRegistration newRegistration) { this.registrations = registrations; this.newRegistration = newRegistration; } void IRegistrationLocationSelectionSyntax.InsteadOf() { if (newRegistration.ComponentType != typeof(TRegistrationType)) { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); } var registrationIndex = registrations.EnsureRegistrationExists(); registrations.entries[registrationIndex] = newRegistration; } void IRegistrationLocationSelectionSyntax.After() { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); var registrationIndex = registrations.EnsureRegistrationExists(); registrations.entries.Insert(registrationIndex + 1, newRegistration); } void IRegistrationLocationSelectionSyntax.Before() { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); var registrationIndex = registrations.EnsureRegistrationExists(); registrations.entries.Insert(registrationIndex, newRegistration); } void IRegistrationLocationSelectionSyntax.OnBottom() { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); registrations.entries.Add(newRegistration); } void IRegistrationLocationSelectionSyntax.OnTop() { 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) ); } } } }