using System; using System.Collections.Generic; using System.Linq; using Chernobyl.Collections.Generic.Event; using Chernobyl.Event; using Chernobyl.Utility; using Chernobyl.Values; namespace Chernobyl.Collections.Generic.Hierarchy { /// /// A type that is used to maintain a parent-child relationship between two /// objects. /// /// The type whose children is held by this /// . This type must be a reference type because a /// value type would not work properly with this class (since you can't access /// the parent from ). /// The type that is the value held by this /// . public class Children : Collection where TParent : class { /// /// Initializes a new instance of the /// class. /// /// The instance whose children is being held by /// this. /// A method that is able to get the parent of a /// type. The argument passed to the /// method is the child whose parent is to be returned. /// Thrown if any of the parameters /// provided are null. public Children(TParent parent, Func> getParent) : this(parent, getParent, new DecoratingEventList()) {} /// /// Initializes a new instance of the /// class. /// /// The instance whose children is being held by /// this. /// The implementation of this /// . /// A method that is able to get the parent of a /// type. The argument passed to the /// method is the child whose parent is to be returned. /// Thrown if any of the parameters /// provided are null. /// Thrown if items are contained /// within . public Children(TParent parent, Func> getParent, IEventCollection implementation) { parent.ThrowIfNull("parent"); getParent.ThrowIfNull("getParent"); implementation.ThrowIfNull("implementation"); if(implementation.Any()) throw new ArgumentException("The implementation provided cannot " + "contain items.", "implementation"); _parent = parent; _getParent = getParent; _implementation = implementation; _implementation.ItemsAdded += OnChildrenAdded; _implementation.ItemsRemoved += OnChildrenRemoved; } /// /// An event handler that is should be invoked when the /// type specified in the constructor /// of this instance gains a new . /// This method sets the parent of the instances specified in /// . /// /// The instance that generated the event. /// The /// instance containing the event data. void OnChildrenAdded(object sender, ItemsEventArgs e) { foreach (TChild child in e.Items) _getParent(child).Value = _parent; } /// /// An event handler that is invoked when /// specified in the constructor of this instance loses a /// . This method sets /// the parent of the removed children (the instances specified in /// ) to null if their parent is still set to /// the instance provided in this /// instances contructor. /// /// The instance that generated the event. /// The /// instance containing the event data. void OnChildrenRemoved(object sender, ItemsEventArgs e) { foreach (TChild child in e.Items) { IDynamicContainer parent = _getParent(child); if (parent.Value == _parent) parent.Value = null; } } /// /// The implementation of this . /// protected override ICollection Implementation { get { return _implementation; } } /// /// The instance whose children is being held by this. /// readonly TParent _parent; /// /// A method that is able to get the parent of a /// type. The argument passed to the /// method is the child whose parent is to be returned. /// readonly Func> _getParent; /// /// The backing field to . /// readonly IEventCollection _implementation; } /// /// A type that is used to maintain a parent-child relationship between two /// types. This type performs the same job as /// but is easier to create for /// types. /// /// The reference type that /// makes up the types contained in the hierarchy. public class HierarchalChildren : Children where T : class, IHierarchal { /// /// Initializes a new instance of the class. /// /// The instance whose children is being held by /// this. public HierarchalChildren(T parent) : base(parent, child => child.Parent) {} } }