using System;
using System.Collections.Generic;
using System.Linq;
using Chernobyl.Event;
using Chernobyl.Utility;
namespace Chernobyl.Collections.Generic.Event
{
///
/// Behaves like the method
///
/// in that it projects each element of a sequence to an
/// and flattens the resulting sequences
/// into one sequence but this type continuously updates itself based on the
/// changes to the original source sequence and the sequences returned by the
/// provided selector method.
///
/// The type of the elements of source.
/// The type of the elements of the sequence
/// returned by selector.
public class SelectManyEventEnumerable : DecoratingEventEnumerable
{
///
/// Initializes a new instance of the
/// class.
///
/// A sequence of values to project.
/// A transform function to apply to each element.
public SelectManyEventEnumerable(IEventEnumerable source,
Func> selector)
: this(source, selector, new List())
{}
///
/// Initializes a new instance of the
/// class.
///
/// A sequence of values to project.
/// A transform function to apply to each element.
/// The instance that will implement the
/// side of this type.
private SelectManyEventEnumerable(IEventEnumerable source,
Func> selector,
ICollection implementation)
: base(implementation)
{
selector.ThrowIfNull("selector");
source.ThrowIfNull("source");
_implementation = implementation;
_selector = selector;
Source = source;
}
///
/// A sequence of values to project.
///
public IEventEnumerable Source
{
get { return _source; }
private set
{
IEventEnumerable previousValue = _source;
_source = value;
if (previousValue != null)
{
previousValue.ItemsAdded += OnSourcesAdded;
previousValue.ItemsRemoved += OnSourcesRemoved;
OnSourcesRemoved(this, new ItemsEventArgs(previousValue.ToArray()));
}
if (_source != null)
{
OnSourcesAdded(this, new ItemsEventArgs(_source.ToArray()));
_source.ItemsAdded += OnSourcesAdded;
_source.ItemsRemoved += OnSourcesRemoved;
}
}
}
///
/// An event handler that is invoked when new sources have been added to
/// . This method applies the
/// method to gather results and also ensures the addition/removal of
/// results is reported later on.
///
/// The sender of the event.
/// The event data containing the items added.
void OnSourcesAdded(object sender, ItemsEventArgs e)
{
foreach (TSource source in e.Items)
{
IEventEnumerable results = _selector(source);
if(results.Any())
OnResultsAdded(this, new ItemsEventArgs(results.ToArray()));
results.ItemsAdded += OnResultsAdded;
results.ItemsRemoved += OnResultsRemoved;
}
}
///
/// An event handler that is invoked when new sources have been removed from
/// . This method applies the
/// method to gather results that are to be removed and ensures results
/// are no longer be listened to for additions/removals.
///
/// The sender of the event.
/// The event data containing the items removed.
void OnSourcesRemoved(object sender, ItemsEventArgs e)
{
foreach (TSource source in e.Items)
{
IEventEnumerable results = _selector(source);
results.ItemsAdded -= OnResultsAdded;
results.ItemsRemoved -= OnResultsRemoved;
if(results.Any())
OnResultsRemoved(this, new ItemsEventArgs(results.ToArray()));
}
}
///
/// An event handler that is invoked when sources have added new
/// instances. This method collects the
/// new instances and reports them as having been added.
///
/// The sender of the event.
/// The event data containing the items added.
void OnResultsAdded(object sender, ItemsEventArgs e)
{
_implementation.AddRange(e.Items);
if (ItemsAddedHandler != null)
ItemsAddedHandler(this, e);
}
///
/// An event handler that is invoked when sources have removed
/// instances. This method removes the
/// instances and reports them as having been removed.
///
/// The sender of the event.
/// The event data containing the items added.
void OnResultsRemoved(object sender, ItemsEventArgs e)
{
_implementation.RemoveRange(e.Items);
if (ItemsRemovedHandler != null)
ItemsRemovedHandler(this, e);
}
///
/// The instance that will implement the
/// side of this type.
///
readonly ICollection _implementation;
///
/// A transform function to apply to each element.
///
readonly Func> _selector;
///
/// The backing field to .
///
IEventEnumerable _source;
}
///
/// Utility methods that work on or with
/// .
///
public static class SelectManyEventEnumerableExtensions
{
///
/// Behaves like the method
///
/// in that it projects each element of a sequence to an
/// and flattens the resulting sequences
/// into one sequence but the instance returned by this method continuously
/// updates itself based on the changes to the original source sequence
/// and the sequences returned by the provided selector method.
///
/// The type of the elements of source.
/// The type of the elements of the sequence
/// returned by selector.
/// A sequence of values to project.
/// A transform function to apply to each element.
/// An whose elements are the
/// result of invoking the one-to-many transform function on each element
/// of the input sequence and will continuously update itself to remain
/// that way.
public static IEventEnumerable SelectMany(
this IEventEnumerable source,
Func> selector)
{
return new SelectManyEventEnumerable(source, selector);
}
}
}