#region License Information
/* HeuristicLab
* Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using HEAL.Attic;
using HeuristicLab.Common;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace HeuristicLab.Collections {
[StorableType("EBD47A4A-848B-487C-B996-2CA2A509A488")]
[Serializable]
public class ObservableList : IObservableList {
[Storable]
protected List list;
#region Properties
public int Capacity {
get { return list.Capacity; }
set {
if (list.Capacity != value) {
list.Capacity = value;
OnPropertyChanged("Capacity");
}
}
}
public int Count {
get { return list.Count; }
}
bool ICollection.IsReadOnly {
get { return ((ICollection)list).IsReadOnly; }
}
public T this[int index] {
get {
return list[index];
}
set {
T item = list[index];
if (!((item == null) && (value == null)) && ((item == null) || (!item.Equals(value)))) {
list[index] = value;
OnItemsReplaced(new IndexedItem[] { new IndexedItem(index, value) }, new IndexedItem[] { new IndexedItem(index, item) });
OnPropertyChanged("Item[]");
}
}
}
#endregion
#region Constructors
public ObservableList() {
list = new List();
}
public ObservableList(int capacity) {
list = new List(capacity);
}
public ObservableList(IEnumerable collection) {
list = new List(collection);
}
[StorableConstructor]
protected ObservableList(StorableConstructorFlag _) { }
#endregion
#region Access
public List GetRange(int index, int count) {
return list.GetRange(index, count);
}
public bool Contains(T item) {
return list.Contains(item);
}
public int IndexOf(T item) {
return list.IndexOf(item);
}
public int IndexOf(T item, int index) {
return list.IndexOf(item, index);
}
public int IndexOf(T item, int index, int count) {
return list.IndexOf(item, index, count);
}
public int LastIndexOf(T item) {
return list.LastIndexOf(item);
}
public int LastIndexOf(T item, int index) {
return list.LastIndexOf(item, index);
}
public int LastIndexOf(T item, int index, int count) {
return list.LastIndexOf(item, index, count);
}
public int BinarySearch(T item) {
return list.BinarySearch(item);
}
public int BinarySearch(T item, IComparer comparer) {
return list.BinarySearch(item, comparer);
}
public int BinarySearch(int index, int count, T item, IComparer comparer) {
return list.BinarySearch(index, count, item, comparer);
}
public bool Exists(Predicate match) {
return list.Exists(match);
}
public T Find(Predicate match) {
return list.Find(match);
}
public List FindAll(Predicate match) {
return list.FindAll(match);
}
public T FindLast(Predicate match) {
return list.FindLast(match);
}
public int FindIndex(Predicate match) {
return list.FindIndex(match);
}
public int FindIndex(int startIndex, Predicate match) {
return list.FindIndex(startIndex, match);
}
public int FindIndex(int startIndex, int count, Predicate match) {
return list.FindIndex(startIndex, count, match);
}
public int FindLastIndex(Predicate match) {
return list.FindLastIndex(match);
}
public int FindLastIndex(int startIndex, Predicate match) {
return list.FindLastIndex(startIndex, match);
}
public int FindLastIndex(int startIndex, int count, Predicate match) {
return list.FindLastIndex(startIndex, count, match);
}
#endregion
#region Manipulation
public void Add(T item) {
int capacity = list.Capacity;
list.Add(item);
OnItemsAdded(new IndexedItem[] { new IndexedItem(list.Count - 1, item) });
OnItemsAdded(new T[] { item });
if (list.Capacity != capacity)
OnPropertyChanged("Capacity");
OnPropertyChanged("Item[]");
OnPropertyChanged("Count");
}
public void AddRange(IEnumerable collection) {
int capacity = list.Capacity;
int index = list.Count;
List> items = new List>();
foreach (T item in collection) {
items.Add(new IndexedItem(index, item));
index++;
}
list.AddRange(items.Select(x => x.Value));
if (items.Count > 0) {
OnItemsAdded(items);
OnItemsAdded(items.Select(x => x.Value));
if (list.Capacity != capacity)
OnPropertyChanged("Capacity");
OnPropertyChanged("Item[]");
OnPropertyChanged("Count");
}
}
public void Insert(int index, T item) {
int capacity = list.Capacity;
list.Insert(index, item);
OnItemsAdded(new IndexedItem[] { new IndexedItem(index, item) });
OnItemsAdded(new T[] { item });
if (list.Capacity != capacity)
OnPropertyChanged("Capacity");
OnPropertyChanged("Item[]");
OnPropertyChanged("Count");
}
public void InsertRange(int index, IEnumerable collection) {
int capacity = list.Capacity;
list.InsertRange(index, collection);
List> items = new List>();
foreach (T item in collection) {
items.Add(new IndexedItem(index, item));
index++;
}
if (items.Count > 0) {
OnItemsAdded(items);
OnItemsAdded(collection);
if (list.Capacity != capacity)
OnPropertyChanged("Capacity");
OnPropertyChanged("Item[]");
OnPropertyChanged("Count");
}
}
///
/// Performs a Clear and an AddRange, but does not fire separate events for those operations
///
///
public void Replace(IEnumerable collection) {
List> oldItems = null;
if (list.Any()) oldItems = list.Select((x, i) => new IndexedItem(i, x)).ToList();
else oldItems = new List>();
int oldCapacity = list.Capacity;
list.Clear();
list.AddRange(collection);
List> items = null;
if (list.Any()) items = list.Select((x, i) => new IndexedItem(i, x)).ToList();
else items = new List>();
OnItemsReplaced(items, oldItems);
if (oldCapacity != list.Capacity) OnPropertyChanged("Capacity");
OnPropertyChanged("Item[]");
if (oldItems.Count != items.Count) OnPropertyChanged("Count");
}
public bool Remove(T item) {
int index = list.IndexOf(item);
if (index != -1) {
list.RemoveAt(index);
OnItemsRemoved(new IndexedItem[] { new IndexedItem(index, item) });
OnItemsRemoved(new T[] { item });
OnPropertyChanged("Item[]");
OnPropertyChanged("Count");
return true;
}
return false;
}
public int RemoveAll(Predicate match) {
if (match == null) throw new ArgumentNullException();
List> indexedItems = new List>();
List items = new List();
for (int i = 0; i < list.Count; i++) {
if (match(list[i])) {
indexedItems.Add(new IndexedItem(i, list[i]));
items.Add(list[i]);
}
}
int result = 0;
if (indexedItems.Count > 0) {
result = list.RemoveAll(match);
OnItemsRemoved(indexedItems);
OnItemsRemoved(items);
OnPropertyChanged("Item[]");
OnPropertyChanged("Count");
}
return result;
}
public void RemoveAt(int index) {
T item = list[index];
list.RemoveAt(index);
OnItemsRemoved(new IndexedItem[] { new IndexedItem(index, item) });
OnItemsRemoved(new T[] { item });
OnPropertyChanged("Item[]");
OnPropertyChanged("Count");
}
public void RemoveRange(int index, int count) {
if (count > 0) {
IndexedItem[] indexedItems = GetIndexedItems(index, count);
T[] items = new T[count];
list.CopyTo(index, items, 0, count);
list.RemoveRange(index, count);
OnItemsRemoved(indexedItems);
OnItemsRemoved(items);
OnPropertyChanged("Item[]");
OnPropertyChanged("Count");
}
}
public void Clear() {
if (list.Count > 0) {
IndexedItem[] indexedItems = GetIndexedItems();
T[] items = list.ToArray();
list.Clear();
OnCollectionReset(new IndexedItem[0], indexedItems);
OnCollectionReset(new T[0], items);
OnPropertyChanged("Item[]");
OnPropertyChanged("Count");
}
}
public void Reverse() {
if (list.Count > 1) {
IndexedItem[] oldItems = GetIndexedItems();
list.Reverse();
OnItemsMoved(GetIndexedItems(), oldItems);
OnPropertyChanged("Item[]");
}
}
public void Reverse(int index, int count) {
if (count > 1) {
IndexedItem[] oldItems = GetIndexedItems(index, count);
list.Reverse(index, count);
OnItemsMoved(GetIndexedItems(index, count), oldItems);
OnPropertyChanged("Item[]");
}
}
public void Sort() {
if (list.Count > 1) {
IndexedItem[] oldItems = GetIndexedItems();
list.StableSort();
OnItemsMoved(GetIndexedItems(), oldItems);
OnPropertyChanged("Item[]");
}
}
public void Sort(Comparison comparison) {
if (list.Count > 1) {
IndexedItem[] oldItems = GetIndexedItems();
list.StableSort(comparison);
OnItemsMoved(GetIndexedItems(), oldItems);
OnPropertyChanged("Item[]");
}
}
public void Sort(IComparer comparer) {
if (list.Count > 1) {
IndexedItem[] oldItems = GetIndexedItems();
list.StableSort(comparer);
OnItemsMoved(GetIndexedItems(), oldItems);
OnPropertyChanged("Item[]");
}
}
public void Sort(int index, int count, IComparer comparer) {
if (count > 1) {
IndexedItem[] oldItems = GetIndexedItems(index, count);
list.StableSort(index, count, comparer);
OnItemsMoved(GetIndexedItems(index, count), oldItems);
OnPropertyChanged("Item[]");
}
}
#endregion
#region Conversion
public ReadOnlyObservableList AsReadOnly() {
return new ReadOnlyObservableList(this);
}
public T[] ToArray() {
return list.ToArray();
}
public void CopyTo(T[] array) {
list.CopyTo(array);
}
public void CopyTo(T[] array, int arrayIndex) {
list.CopyTo(array, arrayIndex);
}
public void CopyTo(int index, T[] array, int arrayIndex, int count) {
list.CopyTo(index, array, arrayIndex, count);
}
public List ConvertAll(Converter converter) {
return list.ConvertAll(converter);
}
#endregion
#region Processing
public void ForEach(Action action) {
list.ForEach(action);
}
public bool TrueForAll(Predicate match) {
return list.TrueForAll(match);
}
#endregion
#region Enumeration
public IEnumerator GetEnumerator() {
return list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return list.GetEnumerator();
}
#endregion
#region Helpers
public void TrimExcess() {
int capacity = list.Capacity;
list.TrimExcess();
if (list.Capacity != capacity)
OnPropertyChanged("Capacity");
}
#endregion
#region Events
[field: NonSerialized]
public event CollectionItemsChangedEventHandler> ItemsAdded;
protected virtual void OnItemsAdded(IEnumerable> items) {
CollectionItemsChangedEventHandler> handler = ItemsAdded;
if (handler != null) handler(this, new CollectionItemsChangedEventArgs>(items));
}
[field: NonSerialized]
private event CollectionItemsChangedEventHandler itemsAdded;
event CollectionItemsChangedEventHandler INotifyObservableCollectionItemsChanged.ItemsAdded {
add { itemsAdded += value; }
remove { itemsAdded -= value; }
}
private void OnItemsAdded(IEnumerable items) {
CollectionItemsChangedEventHandler handler = itemsAdded;
if (handler != null) handler(this, new CollectionItemsChangedEventArgs(items));
}
[field: NonSerialized]
public event CollectionItemsChangedEventHandler> ItemsRemoved;
protected virtual void OnItemsRemoved(IEnumerable> items) {
CollectionItemsChangedEventHandler> handler = ItemsRemoved;
if (handler != null) handler(this, new CollectionItemsChangedEventArgs>(items));
}
[field: NonSerialized]
private event CollectionItemsChangedEventHandler itemsRemoved;
event CollectionItemsChangedEventHandler INotifyObservableCollectionItemsChanged.ItemsRemoved {
add { itemsRemoved += value; }
remove { itemsRemoved -= value; }
}
private void OnItemsRemoved(IEnumerable items) {
CollectionItemsChangedEventHandler handler = itemsRemoved;
if (handler != null) handler(this, new CollectionItemsChangedEventArgs(items));
}
[field: NonSerialized]
public event CollectionItemsChangedEventHandler> ItemsReplaced;
protected virtual void OnItemsReplaced(IEnumerable> items, IEnumerable> oldItems) {
CollectionItemsChangedEventHandler> handler = ItemsReplaced;
if (handler != null) handler(this, new CollectionItemsChangedEventArgs>(items, oldItems));
}
[field: NonSerialized]
public event CollectionItemsChangedEventHandler> ItemsMoved;
protected virtual void OnItemsMoved(IEnumerable> items, IEnumerable> oldItems) {
CollectionItemsChangedEventHandler> handler = ItemsMoved;
if (handler != null) handler(this, new CollectionItemsChangedEventArgs>(items, oldItems));
}
[field: NonSerialized]
public event CollectionItemsChangedEventHandler> CollectionReset;
protected virtual void OnCollectionReset(IEnumerable> items, IEnumerable> oldItems) {
CollectionItemsChangedEventHandler> handler = CollectionReset;
if (handler != null) handler(this, new CollectionItemsChangedEventArgs>(items, oldItems));
}
[field: NonSerialized]
private event CollectionItemsChangedEventHandler collectionReset;
event CollectionItemsChangedEventHandler INotifyObservableCollectionItemsChanged.CollectionReset {
add { collectionReset += value; }
remove { collectionReset -= value; }
}
private void OnCollectionReset(IEnumerable items, IEnumerable oldItems) {
CollectionItemsChangedEventHandler handler = collectionReset;
if (handler != null) handler(this, new CollectionItemsChangedEventArgs(items, oldItems));
}
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region Private helpers
private IndexedItem[] GetIndexedItems() {
IndexedItem[] items = new IndexedItem[list.Count];
for (int i = 0; i < list.Count; i++)
items[i] = new IndexedItem(i, list[i]);
return items;
}
private IndexedItem[] GetIndexedItems(int index, int count) {
IndexedItem[] items = new IndexedItem[count];
for (int i = 0; i < count; i++)
items[i] = new IndexedItem(index + i, list[index + i]);
return items;
}
#endregion
}
}