219 lines
7.4 KiB
C#
219 lines
7.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Specialized;
|
|
using System.Linq;
|
|
using System.Runtime.Serialization;
|
|
|
|
namespace Umbraco.Core.Models
|
|
{
|
|
/// <summary>
|
|
/// Represents a collection of property values.
|
|
/// </summary>
|
|
[Serializable]
|
|
[DataContract(IsReference = true)]
|
|
public class PropertyCollection : KeyedCollection<string, Property>, INotifyCollectionChanged, IDeepCloneable
|
|
{
|
|
private readonly object _addLocker = new object();
|
|
internal Action OnAdd;
|
|
internal Func<Property, bool> AdditionValidator { get; set; }
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="PropertyCollection"/> class.
|
|
/// </summary>
|
|
internal PropertyCollection()
|
|
: base(StringComparer.InvariantCultureIgnoreCase)
|
|
{ }
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="PropertyCollection"/> class.
|
|
/// </summary>
|
|
/// <param name="additionValidator">A function validating added properties.</param>
|
|
internal PropertyCollection(Func<Property, bool> additionValidator)
|
|
: this()
|
|
{
|
|
AdditionValidator = additionValidator;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="PropertyCollection"/> class.
|
|
/// </summary>
|
|
public PropertyCollection(IEnumerable<Property> properties)
|
|
: this()
|
|
{
|
|
Reset(properties);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Replaces all properties, whilst maintaining validation delegates.
|
|
/// </summary>
|
|
internal void Reset(IEnumerable<Property> properties)
|
|
{
|
|
Clear();
|
|
foreach (var property in properties)
|
|
Add(property);
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Replaces the property at the specified index with the specified property.
|
|
/// </summary>
|
|
protected override void SetItem(int index, Property property)
|
|
{
|
|
base.SetItem(index, property);
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, property, index));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes the property at the specified index.
|
|
/// </summary>
|
|
protected override void RemoveItem(int index)
|
|
{
|
|
var removed = this[index];
|
|
base.RemoveItem(index);
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inserts the specified property at the specified index.
|
|
/// </summary>
|
|
protected override void InsertItem(int index, Property property)
|
|
{
|
|
base.InsertItem(index, property);
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, property));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes all properties.
|
|
/// </summary>
|
|
protected override void ClearItems()
|
|
{
|
|
base.ClearItems();
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds or update a property.
|
|
/// </summary>
|
|
internal new void Add(Property property)
|
|
{
|
|
lock (_addLocker) // TODO: why are we locking here and not everywhere else?!
|
|
{
|
|
var key = GetKeyForItem(property);
|
|
if (key != null)
|
|
{
|
|
if (Contains(key))
|
|
{
|
|
// transfer id and values if ...
|
|
var existing = this[key];
|
|
|
|
if (property.Id == 0 && existing.Id != 0)
|
|
property.Id = existing.Id;
|
|
|
|
if (property.Values.Count == 0 && existing.Values.Count > 0)
|
|
property.Values = existing.Values.Select(x => x.Clone()).ToList();
|
|
|
|
// replace existing with property and return,
|
|
// SetItem invokes OnCollectionChanged (but not OnAdd)
|
|
SetItem(IndexOfKey(key), property);
|
|
return;
|
|
}
|
|
}
|
|
|
|
base.Add(property);
|
|
|
|
OnAdd?.Invoke();
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, property));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the index for a specified property alias.
|
|
/// </summary>
|
|
public int IndexOfKey(string key)
|
|
{
|
|
for (var i = 0; i < Count; i++)
|
|
{
|
|
if (this[i].Alias.InvariantEquals(key))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
protected override string GetKeyForItem(Property item)
|
|
{
|
|
return item.Alias;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the property with the specified PropertyType.
|
|
/// </summary>
|
|
internal Property this[PropertyType propertyType]
|
|
{
|
|
get
|
|
{
|
|
return this.FirstOrDefault(x => x.Alias.InvariantEquals(propertyType.Alias));
|
|
}
|
|
}
|
|
|
|
public bool TryGetValue(string propertyTypeAlias, out Property property)
|
|
{
|
|
property = this.FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias));
|
|
return property != null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Occurs when the collection changes.
|
|
/// </summary>
|
|
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
|
|
|
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
|
|
{
|
|
CollectionChanged?.Invoke(this, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensures that the collection contains properties for the specified property types.
|
|
/// </summary>
|
|
protected internal void EnsurePropertyTypes(IEnumerable<PropertyType> propertyTypes)
|
|
{
|
|
if (propertyTypes == null)
|
|
return;
|
|
|
|
foreach (var propertyType in propertyTypes)
|
|
Add(new Property(propertyType));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensures that the collection does not contain properties not in the specified property types.
|
|
/// </summary>
|
|
protected internal void EnsureCleanPropertyTypes(IEnumerable<PropertyType> propertyTypes)
|
|
{
|
|
if (propertyTypes == null)
|
|
return;
|
|
|
|
var propertyTypesA = propertyTypes.ToArray();
|
|
|
|
var thisAliases = this.Select(x => x.Alias);
|
|
var typeAliases = propertyTypesA.Select(x => x.Alias);
|
|
var remove = thisAliases.Except(typeAliases).ToArray();
|
|
foreach (var alias in remove)
|
|
Remove(alias);
|
|
|
|
foreach (var propertyType in propertyTypesA)
|
|
Add(new Property(propertyType));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deep clones.
|
|
/// </summary>
|
|
public object DeepClone()
|
|
{
|
|
var clone = new PropertyCollection();
|
|
foreach (var property in this)
|
|
clone.Add((Property) property.DeepClone());
|
|
return clone;
|
|
}
|
|
}
|
|
}
|