2012-10-03 08:03:45 -02:00
using System ;
using System.Collections.Generic ;
using System.Collections.ObjectModel ;
using System.Collections.Specialized ;
using System.Linq ;
using System.Runtime.Serialization ;
using System.Threading ;
2014-02-20 22:34:54 +11:00
using Umbraco.Core.Models.EntityBase ;
2012-10-03 08:03:45 -02:00
namespace Umbraco.Core.Models
{
2012-10-04 13:05:31 -02:00
/// <summary>
/// Represents a Collection of <see cref="Property"/> objects
/// </summary>
2012-10-03 08:03:45 -02:00
[Serializable]
[DataContract(IsReference = true)]
2014-02-20 22:34:54 +11:00
public class PropertyCollection : KeyedCollection < string , Property > , INotifyCollectionChanged , IDeepCloneable
2012-10-03 08:03:45 -02:00
{
2015-04-08 11:47:16 +10:00
private readonly object _addLocker = new object ( ) ;
2012-10-03 08:03:45 -02:00
internal Action OnAdd ;
internal Func < Property , bool > ValidateAdd { get ; set ; }
internal PropertyCollection ( )
2015-04-01 12:37:28 +01:00
: base ( StringComparer . InvariantCultureIgnoreCase )
{
}
2012-10-03 08:03:45 -02:00
/// <summary>
/// Initializes a new instance of the <see cref="PropertyCollection"/> class with a delegate responsible for validating the addition of <see cref="Property"/> instances.
/// </summary>
/// <param name="validationCallback">The validation callback.</param>
/// <remarks></remarks>
internal PropertyCollection ( Func < Property , bool > validationCallback )
2015-04-01 12:37:28 +01:00
: this ( )
2012-10-03 08:03:45 -02:00
{
ValidateAdd = validationCallback ;
}
public PropertyCollection ( IEnumerable < Property > properties )
2015-04-01 12:37:28 +01:00
: this ( )
2012-10-03 08:03:45 -02:00
{
Reset ( properties ) ;
}
/// <summary>
/// Resets the collection to only contain the <see cref="Property"/> instances referenced in the <paramref name="properties"/> parameter, whilst maintaining
/// any validation delegates such as <see cref="ValidateAdd"/>
/// </summary>
/// <param name="properties">The properties.</param>
/// <remarks></remarks>
internal void Reset ( IEnumerable < Property > 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 )
{
2015-04-08 11:47:16 +10:00
lock ( _addLocker )
2012-10-03 08:03:45 -02:00
{
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 ] ;
2017-09-07 13:13:03 +02:00
if ( item . Id = = 0 & & property . Id ! = 0 )
2012-10-03 08:03:45 -02:00
{
2017-09-07 12:57:06 +02:00
item . Id = property . Id ;
2017-09-07 13:13:03 +02:00
}
if ( item . Value = = null & & property . Value ! = null )
{
2012-10-03 08:03:45 -02:00
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 ) ) ;
}
}
/// <summary>
/// Determines whether this collection contains a <see cref="Property"/> whose alias matches the specified PropertyType.
/// </summary>
/// <param name="propertyTypeAlias">Alias of the PropertyType.</param>
/// <returns><c>true</c> if the collection contains the specified alias; otherwise, <c>false</c>.</returns>
/// <remarks></remarks>
public new bool Contains ( string propertyTypeAlias )
{
2015-04-01 12:37:28 +01:00
return base . Contains ( propertyTypeAlias ) ;
2012-10-03 08:03:45 -02:00
}
public int IndexOfKey ( string key )
{
for ( var i = 0 ; i < this . Count ; i + + )
{
2015-04-01 12:37:28 +01:00
if ( this [ i ] . Alias . InvariantEquals ( key ) )
2012-10-03 08:03:45 -02:00
{
return i ;
}
}
return - 1 ;
}
protected override string GetKeyForItem ( Property item )
{
return item . Alias ;
}
/// <summary>
/// Gets the element with the specified PropertyType.
/// </summary>
///
/// <returns>
/// The element with the specified PropertyType. If an element with the specified PropertyType is not found, an exception is thrown.
/// </returns>
/// <param name="propertyType">The PropertyType of the element to get.</param><exception cref="T:System.ArgumentNullException"><paramref name="propertyType"/> is null.</exception><exception cref="T:System.Collections.Generic.KeyNotFoundException">An element with the specified key does not exist in the collection.</exception>
internal Property this [ PropertyType propertyType ]
{
get
{
2015-04-01 12:37:28 +01:00
return this . FirstOrDefault ( x = > x . Alias . InvariantEquals ( propertyType . Alias ) ) ;
2012-10-03 08:03:45 -02:00
}
}
public event NotifyCollectionChangedEventHandler CollectionChanged ;
protected virtual void OnCollectionChanged ( NotifyCollectionChangedEventArgs args )
{
if ( CollectionChanged ! = null )
{
CollectionChanged ( this , args ) ;
}
}
/// <summary>
/// Ensures that the collection contains Properties for the passed in PropertyTypes
/// </summary>
/// <param name="propertyTypes">List of PropertyType</param>
protected internal void EnsurePropertyTypes ( IEnumerable < PropertyType > propertyTypes )
{
2015-04-01 12:37:28 +01:00
if ( /*!this.Any() &&*/ propertyTypes ! = null )
2012-10-03 08:03:45 -02:00
{
foreach ( var propertyType in propertyTypes )
{
Add ( new Property ( propertyType ) ) ;
}
}
}
/// <summary>
/// Ensures that the collection is cleared from PropertyTypes not in the list of passed in PropertyTypes
/// </summary>
/// <param name="propertyTypes">List of PropertyType</param>
protected internal void EnsureCleanPropertyTypes ( IEnumerable < PropertyType > propertyTypes )
{
2015-04-01 12:37:28 +01:00
if ( propertyTypes ! = null )
2012-10-03 08:03:45 -02:00
{
//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 ) ) ;
}
}
}
2014-02-20 22:34:54 +11:00
/// <summary>
/// Create a deep clone of this property collection
/// </summary>
/// <returns></returns>
2014-04-15 13:52:49 +10:00
public object DeepClone ( )
2014-02-20 22:34:54 +11:00
{
var newList = new PropertyCollection ( ) ;
foreach ( var p in this )
{
2014-04-15 13:52:49 +10:00
newList . Add ( ( Property ) p . DeepClone ( ) ) ;
2014-02-20 22:34:54 +11:00
}
2014-04-15 13:52:49 +10:00
return newList ;
2014-02-20 22:34:54 +11:00
}
2012-10-03 08:03:45 -02:00
}
}