using System; using System.Collections.Generic; using System.Linq; using Chernobyl.Collections.Generic.Event; using Chernobyl.Event; namespace Chernobyl.Collections.Generic.Hierarchy { /// /// Extension and other general methods that are used to help with the /// creation of types that form parent/child hierarchies. /// public static class TreeExtensions { /// /// A type that can be assigned to an event. Specifically it is designed /// to be assigned to an event that is invoked when instances are added /// to a child collection (such as the event /// . When the event handler /// method is invoked, all of the added /// items have their parent set to the parent specified in this type's /// constructor. /// /// The type of the parent. /// The type of the child. public class Parenter { /// /// Initializes a new instance of the /// class. /// /// The parent that is to be set on each of the /// instances that this type is /// notified of. /// A method that is able to set the parent /// of a type. The first argument passed /// to the method is the parent, the second argument is the parent to /// be set. public Parenter(TParent parent, Action setParent) { _parent = parent; _setParent = setParent; } /// /// Performs an implicit conversion from /// /// to . This operator allows this /// type to be implicitly assigned to an event. /// /// The /// to convert. /// The result of the conversion. public static implicit operator EventHandler>(Parenter parenter) { return parenter.OnChildrenAdded; } /// /// 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. public void OnChildrenAdded(object sender, ItemsEventArgs e) { foreach (TChild child in e.Items) _setParent(_parent, child); } /// /// The parent that is to be set on each of the /// instances that this type is /// notified of. /// TParent _parent; /// /// A method that is able to set the parent of a /// type. The first argument passed /// to the method is the parent, and the second argument is the child /// to be set on that parent. /// Action _setParent; } /// /// A type that can be assigned to an event. Specifically it is designed /// to be assigned to an event that is invoked when instances are removed /// from child collection (such as the event /// . When the event handler /// method is invoked, all of the added /// items have their parent set to null but only if their parent is the /// parent specified in this type's constructor. /// /// The type of the parent. This type must be /// a reference type /// The type of the child. public class Unparenter where TParent : class { /// /// Initializes a new instance of the /// class. /// /// The parent of the /// instances that this type is notified of. /// A method that is able to set the parent /// of a type. The first argument passed /// to the method is the parent, the second argument is the parent to /// be set. /// 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. public Unparenter(TParent parent, Action setParent, Func getParent) { _parent = parent; _setParent = setParent; _getParent = getParent; } /// /// Performs an implicit conversion from /// /// to . This operator allows this /// type to be implicitly assigned to an event. /// /// The /// to convert. /// The result of the conversion. public static implicit operator EventHandler>(Unparenter parenter) { return parenter.OnChildrenRemoved; } /// /// 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. public void OnChildrenRemoved(object sender, ItemsEventArgs e) { foreach (TChild child in e.Items.Where(territory => _getParent(territory) == _parent)) _setParent(null, child); } /// /// The parent of the instances that /// this type is notified of. /// TParent _parent; /// /// A method that is able to set the parent of a /// type. The first argument passed /// to the method is the parent, and the second argument is the child /// to be set on that parent. /// Action _setParent; /// /// 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. /// Func _getParent; } /// /// A method that handles parenting for an . /// This method removes the from the /// and adds the child to the /// . Before it does this, however, it /// performs checks to make sure the parenting is valid (i.e. that /// the parent is actually being changed and neither are null). This /// method is used primarily by types attempting to implement child/parent /// hierarchies. /// /// The type of the parent. This type must be /// a reference type. /// The type of the child. /// The instance that is to be removed from /// child collection and added to the /// child collection of . /// The instance that is currently the /// parent of . When this method returns, this /// argument will be equal to the that /// is now the parent of . /// The instance that shall become the parent /// of . /// The method that can retrieve the children /// of the type. /// The first argument passed to the method is the parent whose children /// are to be returned. public static void GetParent(TChild child, ref TParent currentParent, TParent newParent, Func> getChildren) where TParent : class { if (currentParent != newParent) { TParent oldParent = currentParent; currentParent = newParent; if (oldParent != null) getChildren(oldParent).Remove(child); if (currentParent != null && getChildren(currentParent).Contains(child) == false) getChildren(currentParent).Add(child); } } /// /// This method handles the un-parenting of instances in /// from the /// and the parenting of the children in /// to the . Before it does this, however, it /// performs checks to make sure that the parent is actually being /// changed. This method is used primarily by types attempting to /// implement child/parent hierarchies. /// /// The type of the parent. This type must be /// a reference type. /// The type of the child. /// The parent of the instances in /// . /// The instances that are currently the /// children of . When this method returns, this /// argument will be equal to the current children of /// . /// The instances that are to become the children /// of . /// A method that can retrieve the parent of a /// . The argument passed to the method is the /// child. /// A method that can set the parent of a /// . The first argument passed to the method /// is the child, the second argument is the parent to be set. public static void GetChildren(TParent parent, ref ICollection currentChildren, ICollection newChildren, Func getParent, Action setParent) where TParent : class { if (currentChildren != newChildren) { ICollection oldChildren = currentChildren; currentChildren = newChildren; if (oldChildren != null) { // This instance is no longer the parent of these children. // Inform these new children of this. foreach (TChild child in oldChildren.Where(child => getParent(child) == parent)) setParent(child, null); } if (currentChildren != null) { // This instance is now the parent of these children. // Inform these new children of this. foreach (TChild child in currentChildren) setParent(child, parent); } } } } }