#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

207 lines
7.3 KiB

// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// Provides LINQ operators for <see cref="IModelCollection{T}"/>.
/// </summary>
public static class ModelCollectionLinq
{
// A general 'AsObservableCollection()' would be nice; but I don't see any good way
// to implement that without leaking memory.
// The problem is that IModelCollection is unordered; but ObservableCollection requires us to maintain a stable order.
#region OfType / Cast
public static IModelCollection<TResult> OfType<TResult>(this IModelCollection<object> source)
{
return SelectMany(source, item => item is TResult ? new ImmutableModelCollection<TResult>(new[] { (TResult)item }) : ImmutableModelCollection<TResult>.Empty);
}
public static IModelCollection<TResult> Cast<TResult>(this IModelCollection<object> source)
{
return SelectMany(source, item => new ImmutableModelCollection<TResult>(new[] { (TResult)item }));
}
#endregion
#region Where
public static IModelCollection<TSource> Where<TSource>(this IModelCollection<TSource> source, Func<TSource, bool> predicate)
{
return SelectMany(source, item => predicate(item) ? new ImmutableModelCollection<TSource>(new[] { item }) : ImmutableModelCollection<TSource>.Empty);
}
#endregion
#region Select
public static IModelCollection<TResult> Select<TSource, TResult>(this IModelCollection<TSource> source, Func<TSource, TResult> selector)
{
return SelectMany(source, item => new ImmutableModelCollection<TResult>(new[] { selector(item) }));
}
#endregion
#region SelectMany
public static IModelCollection<TResult> SelectMany<TSource, TResult>(this IModelCollection<TSource> input, Func<TSource, IModelCollection<TResult>> selector)
{
return SelectMany(input, selector, (a, b) => b);
}
public static IModelCollection<TResult> SelectMany<TSource, TCollection, TResult>(this IModelCollection<TSource> source, Func<TSource, IModelCollection<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector)
{
if (source == null)
throw new ArgumentNullException("source");
if (collectionSelector == null)
throw new ArgumentNullException("collectionSelector");
if (resultSelector == null)
throw new ArgumentNullException("resultSelector");
return new SelectManyModelCollection<TSource, TCollection, TResult>(source, collectionSelector, resultSelector);
}
sealed class SelectManyModelCollection<TSource, TCollection, TResult> : IModelCollection<TResult>
{
readonly IModelCollection<TSource> source;
readonly Func<TSource, IModelCollection<TCollection>> collectionSelector;
readonly Func<TSource, TCollection, TResult> resultSelector;
List<InputCollection> inputCollections;
public SelectManyModelCollection(IModelCollection<TSource> source, Func<TSource, IModelCollection<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector)
{
this.source = source;
this.collectionSelector = collectionSelector;
this.resultSelector = resultSelector;
}
ModelCollectionChangedEventHandler<TResult> collectionChanged;
public event ModelCollectionChangedEventHandler<TResult> CollectionChanged {
add {
if (value == null)
return;
if (inputCollections == null) {
source.CollectionChanged += OnSourceCollectionChanged;
inputCollections = new List<InputCollection>();
foreach (var item in source) {
var inputCollection = new InputCollection(this, item);
inputCollection.RegisterEvent();
inputCollections.Add(inputCollection);
}
}
collectionChanged += value;
}
remove {
if (collectionChanged == null)
return;
collectionChanged -= value;
if (collectionChanged == null) {
source.CollectionChanged -= OnSourceCollectionChanged;
foreach (var inputCollection in inputCollections) {
inputCollection.UnregisterEvent();
}
inputCollections = null;
}
}
}
void OnCollectionChanged(IReadOnlyCollection<TResult> removedItems, IReadOnlyCollection<TResult> addedItems)
{
if (collectionChanged != null)
collectionChanged(removedItems, addedItems);
}
void OnSourceCollectionChanged(IReadOnlyCollection<TSource> removedItems, IReadOnlyCollection<TSource> addedItems)
{
List<TResult> removedResults = new List<TResult>();
foreach (TSource removedSource in removedItems) {
for (int i = 0; i < inputCollections.Count; i++) {
var inputCollection = inputCollections[i];
if (EqualityComparer<TSource>.Default.Equals(inputCollection.source, removedSource)) {
inputCollection.AddResultsToList(removedResults);
inputCollection.UnregisterEvent();
inputCollections.RemoveAt(i);
break;
}
}
}
List<TResult> addedResults = new List<TResult>();
foreach (TSource addedSource in addedItems) {
var inputCollection = new InputCollection(this, addedSource);
inputCollection.AddResultsToList(addedResults);
inputCollection.RegisterEvent();
inputCollections.Add(inputCollection);
}
OnCollectionChanged(removedResults, addedResults);
}
class InputCollection
{
readonly SelectManyModelCollection<TSource, TCollection, TResult> parent;
internal readonly TSource source;
readonly IModelCollection<TCollection> collection;
public InputCollection(SelectManyModelCollection<TSource, TCollection, TResult> parent, TSource source)
{
this.parent = parent;
this.source = source;
this.collection = parent.collectionSelector(source);
}
public void AddResultsToList(List<TResult> output)
{
foreach (var item in collection) {
output.Add(parent.resultSelector(source, item));
}
}
public void RegisterEvent()
{
collection.CollectionChanged += OnCollectionChanged;
}
public void UnregisterEvent()
{
collection.CollectionChanged -= OnCollectionChanged;
}
IReadOnlyCollection<TResult> GetResults(IReadOnlyCollection<TCollection> itemsCollection)
{
List<TResult> results = new List<TResult>(itemsCollection.Count);
foreach (var item in itemsCollection) {
results.Add(parent.resultSelector(source, item));
}
return results;
}
void OnCollectionChanged(IReadOnlyCollection<TCollection> removedItems, IReadOnlyCollection<TCollection> addedItems)
{
parent.OnCollectionChanged(GetResults(removedItems), GetResults(addedItems));
}
}
IReadOnlyCollection<TResult> IModelCollection<TResult>.CreateSnapshot()
{
return this.ToList();
}
public IEnumerator<TResult> GetEnumerator()
{
return source.AsEnumerable().SelectMany(collectionSelector, resultSelector).GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int Count {
get {
return source.Sum(c => collectionSelector(c).Count);
}
}
}
#endregion
}
}