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);
}
}
}
}
}