using System; using System.Collections.Generic; using LightInject; namespace Umbraco.Core.Composing { /// /// Implements an ordered collection builder. /// /// The type of the builder. /// The type of the collection. /// The type of the items. public abstract class OrderedCollectionBuilderBase : CollectionBuilderBase where TBuilder : OrderedCollectionBuilderBase where TCollection : IBuilderCollection { /// /// Initializes a new instance of the class. /// /// protected OrderedCollectionBuilderBase(IServiceContainer container) : base (container) { } protected abstract TBuilder This { get; } /// /// Clears all types in the collection. /// /// The buidler. public TBuilder Clear() { Configure(types => types.Clear()); return This; } /// /// Appends a type to the collection. /// /// The type to append. /// The builder. public TBuilder Append() where T : TItem { Configure(types => { var type = typeof (T); if (types.Contains(type)) types.Remove(type); types.Add(type); }); return This; } /// /// Appends a type to the collection. /// /// The type to append. /// The builder. public TBuilder Append(Type type) { Configure(types => { EnsureType(type, "register"); if (types.Contains(type)) types.Remove(type); types.Add(type); }); return This; } /// /// Appends types to the collections. /// /// The types to append. /// The builder. public TBuilder Append(IEnumerable types) { Configure(list => { foreach (var type in types) { // would be detected by CollectionBuilderBase when registering, anyways, but let's fail fast EnsureType(type, "register"); if (list.Contains(type)) list.Remove(type); list.Add(type); } }); return This; } /// /// Appends types to the collections. /// /// The types to append. /// The builder. public TBuilder Append(Func> types) { Configure(list => { foreach (var type in types(Container)) { // would be detected by CollectionBuilderBase when registering, anyways, but let's fail fast EnsureType(type, "register"); if (list.Contains(type)) list.Remove(type); list.Add(type); } }); return This; } /// /// Appends a type after another type. /// /// The other type. /// The type to append. /// The builder. /// Throws if both types are identical, or if the other type does not already belong to the collection. public TBuilder AppendAfter() where TAfter : TItem where T : TItem { Configure(types => { var typeAfter = typeof (TAfter); var type = typeof(T); if (typeAfter == type) throw new InvalidOperationException(); var index = types.IndexOf(typeAfter); if (index < 0) throw new InvalidOperationException(); if (types.Contains(type)) types.Remove(type); index = types.IndexOf(typeAfter); // in case removing type changed index types.Insert(index + 1, type); }); return This; } /// /// Inserts a type into the collection. /// /// The type to insert. /// The optional index. /// The builder. /// Throws if the index is out of range. public TBuilder Insert(int index = 0) where T : TItem { Configure(types => { var type = typeof (T); if (types.Contains(type)) types.Remove(type); types.Insert(index, type); }); return This; } /// /// Inserts a type into the collection. /// /// The type to insert. /// The builder. /// Throws if the index is out of range. public TBuilder Insert(Type type) { return Insert(0, type); } /// /// Inserts a type into the collection. /// /// The index. /// The type to insert. /// The builder. /// Throws if the index is out of range. public TBuilder Insert(int index, Type type) { Configure(types => { EnsureType(type, "register"); if (types.Contains(type)) types.Remove(type); types.Insert(index, type); }); return This; } /// /// Inserts a type before another type. /// /// The other type. /// The type to insert. /// The builder. /// Throws if both types are identical, or if the other type does not already belong to the collection. public TBuilder InsertBefore() where TBefore : TItem where T : TItem { Configure(types => { var typeBefore = typeof(TBefore); var type = typeof(T); if (typeBefore == type) throw new InvalidOperationException(); var index = types.IndexOf(typeBefore); if (index < 0) throw new InvalidOperationException(); if (types.Contains(type)) types.Remove(type); index = types.IndexOf(typeBefore); // in case removing type changed index types.Insert(index, type); }); return This; } /// /// Inserts a type before another type. /// /// The other type. /// The type to insert. /// The builder. /// Throws if both types are identical, or if the other type does not already belong to the collection. public TBuilder InsertBefore(Type typeBefore, Type type) { Configure(types => { EnsureType(typeBefore, "find"); EnsureType(type, "register"); if (typeBefore == type) throw new InvalidOperationException(); var index = types.IndexOf(typeBefore); if (index < 0) throw new InvalidOperationException(); if (types.Contains(type)) types.Remove(type); index = types.IndexOf(typeBefore); // in case removing type changed index types.Insert(index, type); }); return This; } /// /// Removes a type from the collection. /// /// The type to remove. /// The builder. public TBuilder Remove() where T : TItem { Configure(types => { var type = typeof (T); if (types.Contains(type)) types.Remove(type); }); return This; } /// /// Removes a type from the collection. /// /// The type to remove. /// The builder. public TBuilder Remove(Type type) { Configure(types => { EnsureType(type, "remove"); if (types.Contains(type)) types.Remove(type); }); return This; } /// /// Replaces a type in the collection. /// /// The type to replace. /// The type to insert. /// The builder. /// Throws if the type to replace does not already belong to the collection. public TBuilder Replace() where TReplaced : TItem where T : TItem { Configure(types => { var typeReplaced = typeof(TReplaced); var type = typeof(T); if (typeReplaced == type) return; var index = types.IndexOf(typeReplaced); if (index < 0) throw new InvalidOperationException(); if (types.Contains(type)) types.Remove(type); index = types.IndexOf(typeReplaced); // in case removing type changed index types.Insert(index, type); types.Remove(typeReplaced); }); return This; } /// /// Replaces a type in the collection. /// /// The type to replace. /// The type to insert. /// The builder. /// Throws if the type to replace does not already belong to the collection. public TBuilder Replace(Type typeReplaced, Type type) { Configure(types => { EnsureType(typeReplaced, "find"); EnsureType(type, "register"); if (typeReplaced == type) return; var index = types.IndexOf(typeReplaced); if (index < 0) throw new InvalidOperationException(); if (types.Contains(type)) types.Remove(type); index = types.IndexOf(typeReplaced); // in case removing type changed index types.Insert(index, type); types.Remove(typeReplaced); }); return This; } } }