#region License Information
/* HeuristicLab
* Copyright (C) 2002-2018 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 System;
using System.Collections.Generic;
using System.Linq;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
namespace HeuristicLab.Collections {
[Serializable]
[StorableClass]
public abstract class ObservableKeyedList : ObservableList, IObservableKeyedList {
private Dictionary dict;
[Storable]
protected Dictionary Dictionary {
get { return dict; }
private set { dict = value; }
}
protected IEqualityComparer Comparer {
get { return dict.Comparer; }
}
public TItem this[TKey key] {
get { return dict[key]; }
}
#region constructors
protected ObservableKeyedList()
: base() {
dict = new Dictionary();
}
protected ObservableKeyedList(int capacity)
: base(capacity) {
dict = new Dictionary(capacity);
}
protected ObservableKeyedList(IEqualityComparer comparer)
: base() {
dict = new Dictionary(comparer);
}
protected ObservableKeyedList(int capacity, IEqualityComparer comparer)
: base(capacity) {
dict = new Dictionary(capacity, comparer);
}
[StorableConstructor]
protected ObservableKeyedList(bool deserializing) : base(deserializing) { }
#endregion
protected abstract TKey GetKeyForItem(TItem item);
protected void UpdateItemKey(TItem item) {
if (item == null) throw new ArgumentNullException();
var dictionaryItem = dict.FirstOrDefault(i => i.Value.Equals(item));
if (dictionaryItem.Equals(default(KeyValuePair))) throw new ArgumentException("Item not found");
dict.Remove(dictionaryItem.Key);
dict.Add(GetKeyForItem(item), item);
int index = IndexOf(item);
OnItemsReplaced(new[] { new IndexedItem(index, item) }, new[] { new IndexedItem(index, item) });
OnItemsReplaced(new[] { item }, new[] { item });
OnPropertyChanged("Item[]");
}
#region methods
public new ReadOnlyObservableKeyedList AsReadOnly() {
return new ReadOnlyObservableKeyedList(this);
}
public bool ContainsKey(TKey key) {
return dict.ContainsKey(key);
}
public new bool Contains(TItem item) {
TKey key = GetKeyForItem(item);
return ContainsKey(key);
}
public bool TryGetValue(TKey key, out TItem item) {
return dict.TryGetValue(key, out item);
}
public bool Remove(TKey key) {
TItem item;
if (!TryGetValue(key, out item)) return false;
return base.Remove(item);
}
#endregion
#region Events
[field: NonSerialized]
private event CollectionItemsChangedEventHandler itemsReplaced;
event CollectionItemsChangedEventHandler INotifyObservableKeyedCollectionItemsChanged.ItemsReplaced {
add { itemsReplaced += value; }
remove { itemsReplaced -= value; }
}
private void OnItemsReplaced(IEnumerable items, IEnumerable oldItems) {
var handler = itemsReplaced;
if (handler != null) handler(this, new CollectionItemsChangedEventArgs(items, oldItems));
}
protected override void OnItemsAdded(IEnumerable> items) {
UpdateDictionary(items, Enumerable.Empty>());
base.OnItemsAdded(items);
}
protected override void OnItemsRemoved(IEnumerable> items) {
UpdateDictionary(Enumerable.Empty>(), items);
base.OnItemsRemoved(items);
}
protected override void OnItemsReplaced(IEnumerable> items, IEnumerable> oldItems) {
UpdateDictionary(items, oldItems);
base.OnItemsReplaced(items, oldItems);
}
protected override void OnCollectionReset(IEnumerable> items, IEnumerable> oldItems) {
UpdateDictionary(items, oldItems);
base.OnCollectionReset(items, oldItems);
}
#endregion
#region helper methods
private void UpdateDictionary(IEnumerable> items, IEnumerable> oldItems) {
foreach (var item in oldItems)
dict.Remove(GetKeyForItem(item.Value));
bool duplicateKeyFound = false;
foreach (var item in items) {
TKey key = GetKeyForItem(item.Value);
if (dict.ContainsKey(key)) {
duplicateKeyFound = true;
break;
}
dict.Add(key, item.Value);
}
//restore old state of list and dictionary
if (duplicateKeyFound) {
foreach (var item in items.OrderByDescending(i => i.Index)) {
list.RemoveAt(item.Index);
}
foreach (var item in items.Select(i => i.Value).Except(list)) {
TKey key = GetKeyForItem(item);
dict.Remove(key);
}
foreach (var item in oldItems.OrderByDescending(i => i.Index)) {
list.Insert(item.Index, item.Value);
TKey key = GetKeyForItem(item.Value);
dict.Add(key, item.Value);
}
throw new ArgumentException("An element with the same key already exists.");
}
}
#endregion
}
}