using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
///
/// Represents a Collection of objects
///
[Serializable]
[DataContract(IsReference = true)]
public class PropertyCollection : KeyedCollection, INotifyCollectionChanged, IDeepCloneable
{
private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim();
internal Action OnAdd;
internal Func ValidateAdd { get; set; }
internal PropertyCollection()
{}
///
/// Initializes a new instance of the class with a delegate responsible for validating the addition of instances.
///
/// The validation callback.
///
internal PropertyCollection(Func validationCallback)
{
ValidateAdd = validationCallback;
}
public PropertyCollection(IEnumerable properties)
{
Reset(properties);
}
///
/// Resets the collection to only contain the instances referenced in the parameter, whilst maintaining
/// any validation delegates such as
///
/// The properties.
///
internal void Reset(IEnumerable properties)
{
Clear();
properties.ForEach(Add);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected override void SetItem(int index, Property item)
{
base.SetItem(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
protected override void RemoveItem(int index)
{
var removed = this[index];
base.RemoveItem(index);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
}
protected override void InsertItem(int index, Property item)
{
base.InsertItem(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
protected override void ClearItems()
{
base.ClearItems();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
internal new void Add(Property item)
{
using (new WriteLock(_addLocker))
{
var key = GetKeyForItem(item);
if (key != null)
{
var exists = this.Contains(key);
if (exists)
{
//NOTE: Consider checking type before value is set: item.PropertyType.DataTypeId == property.PropertyType.DataTypeId
//Transfer the existing value to the new property
var property = this[key];
if (item.Value == null && property.Value != null)
{
item.Value = property.Value;
}
SetItem(IndexOfKey(key), item);
return;
}
}
base.Add(item);
OnAdd.IfNotNull(x => x.Invoke());//Could this not be replaced by a Mandate/Contract for ensuring item is not null
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
}
///
/// Determines whether this collection contains a whose alias matches the specified PropertyType.
///
/// Alias of the PropertyType.
/// true if the collection contains the specified alias; otherwise, false.
///
public new bool Contains(string propertyTypeAlias)
{
return this.Any(x => x.Alias == propertyTypeAlias);
}
public int IndexOfKey(string key)
{
for (var i = 0; i < this.Count; i++)
{
if (this[i].Alias == key)
{
return i;
}
}
return -1;
}
protected override string GetKeyForItem(Property item)
{
return item.Alias;
}
///
/// Gets the element with the specified PropertyType.
///
///
///
/// The element with the specified PropertyType. If an element with the specified PropertyType is not found, an exception is thrown.
///
/// The PropertyType of the element to get. is null.An element with the specified key does not exist in the collection.
internal Property this[PropertyType propertyType]
{
get
{
return this.FirstOrDefault(x => x.Alias == propertyType.Alias);
}
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
if (CollectionChanged != null)
{
CollectionChanged(this, args);
}
}
///
/// Ensures that the collection contains Properties for the passed in PropertyTypes
///
/// List of PropertyType
protected internal void EnsurePropertyTypes(IEnumerable propertyTypes)
{
if(/*!this.Any() &&*/ propertyTypes != null)
{
foreach (var propertyType in propertyTypes)
{
Add(new Property(propertyType));
}
}
}
///
/// Ensures that the collection is cleared from PropertyTypes not in the list of passed in PropertyTypes
///
/// List of PropertyType
protected internal void EnsureCleanPropertyTypes(IEnumerable propertyTypes)
{
if(propertyTypes != null)
{
//Remove PropertyTypes that doesn't exist in the list of new PropertyTypes
var aliases = this.Select(p => p.Alias).Except(propertyTypes.Select(x => x.Alias)).ToList();
foreach (var alias in aliases)
{
Remove(alias);
}
//Add new PropertyTypes from the list of passed in PropertyTypes
foreach (var propertyType in propertyTypes)
{
Add(new Property(propertyType));
}
}
}
///
/// Create a deep clone of this property collection
///
///
public object DeepClone()
{
var newList = new PropertyCollection();
foreach (var p in this)
{
newList.Add((Property)p.DeepClone());
}
return newList;
}
}
}