<PackageReference Include="Castle.Core" Version="3.0.0.2001" />

DictionaryAdapterBase

using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Linq; namespace Castle.Components.DictionaryAdapter { public abstract class DictionaryAdapterBase : IDictionaryAdapter, IDictionaryEdit, IEditableObject, IRevertibleChangeTracking, IChangeTracking, IDictionaryNotify, INotifyPropertyChanging, INotifyPropertyChanged, IDictionaryCreate, IDictionaryValidate, IDataErrorInfo { private struct Edit { public readonly PropertyDescriptor Property; public object PropertyValue; public Edit(PropertyDescriptor property, object propertyValue) { Property = property; PropertyValue = propertyValue; } } private class SuppressEditingScope : IDisposable { private readonly DictionaryAdapterBase adapter; public SuppressEditingScope(DictionaryAdapterBase adapter) { this.adapter = adapter; this.adapter.SuppressEditing(); } public void Dispose() { adapter.ResumeEditing(); } } private class SuppressNotificationsScope : IDisposable { private readonly DictionaryAdapterBase adapter; public SuppressNotificationsScope(DictionaryAdapterBase adapter) { this.adapter = adapter; this.adapter.SuppressNotifications(); } public void Dispose() { adapter.ResumeNotifications(); } } public class TrackPropertyChangeScope : IDisposable { private readonly DictionaryAdapterBase adapter; private readonly PropertyDescriptor property; private readonly object existingValue; private IDictionary<PropertyDescriptor, object> readonlyProperties; public TrackPropertyChangeScope(DictionaryAdapterBase adapter) { this.adapter = adapter; readonlyProperties = adapter.This.Properties.Values.Where(delegate(PropertyDescriptor pd) { if (pd.Property.CanWrite) return pd.IsDynamicProperty; return true; }).ToDictionary((PropertyDescriptor pd) => pd, (PropertyDescriptor pd) => GetEffectivePropertyValue(pd)); } public TrackPropertyChangeScope(DictionaryAdapterBase adapter, PropertyDescriptor property, object existingValue) : this(adapter) { this.property = property; this.existingValue = existingValue; existingValue = adapter.GetProperty(property.PropertyName, true); } public bool Notify() { if (readonlyTrackingScope == this) { readonlyTrackingScope = null; return NotifyReadonly(); } object effectivePropertyValue = GetEffectivePropertyValue(property); if (NotifyIfChanged(property, existingValue, effectivePropertyValue)) { if (readonlyTrackingScope == null) NotifyReadonly(); return true; } return false; } private bool NotifyReadonly() { bool flag = false; foreach (KeyValuePair<PropertyDescriptor, object> readonlyProperty in readonlyProperties) { PropertyDescriptor key = readonlyProperty.Key; object effectivePropertyValue = GetEffectivePropertyValue(key); flag |= NotifyIfChanged(key, readonlyProperty.Value, effectivePropertyValue); } adapter.Invalidate(); return flag; } private bool NotifyIfChanged(PropertyDescriptor descriptor, object oldValue, object newValue) { if (!object.Equals(oldValue, newValue)) { adapter.NotifyPropertyChanged(descriptor, oldValue, newValue); return true; } return false; } private object GetEffectivePropertyValue(PropertyDescriptor property) { object value = adapter.GetProperty(property.PropertyName, true); if ((value != null) & property.IsDynamicProperty) value = ((IDynamicValue)value).GetValue(); return value; } public void Dispose() { Notify(); } } private int suppressEditingCount; private Stack<Dictionary<string, Edit>> updates; private HashSet<IEditableObject> editDependencies; private int suppressNotificationCount; private bool propagateChildNotifications = true; private Dictionary<object, object> composedChildNotifications; [ThreadStatic] private static TrackPropertyChangeScope readonlyTrackingScope; private ICollection<IDictionaryValidator> validators; public abstract DictionaryAdapterMeta Meta { get; } public DictionaryAdapterInstance This { get; set; } public bool CanEdit { get { if (suppressEditingCount == 0) return updates != null; return false; } set { updates = (value ? new Stack<Dictionary<string, Edit>>() : null); } } public bool IsEditing { get { if (CanEdit && updates != null) return updates.Count > 0; return false; } } public bool SupportsMultiLevelEdit { get; set; } public bool IsChanged { get { if (IsEditing && updates.Any((Dictionary<string, Edit> level) => level.Count > 0)) return true; return (from prop in This.Properties.Values where typeof(IChangeTracking).IsAssignableFrom(prop.PropertyType) select GetProperty(prop.PropertyName, true)).Cast<IChangeTracking>().Any((IChangeTracking track) => track?.IsChanged ?? false); } } public bool CanNotify { get; set; } public bool ShouldNotify { get { if (CanNotify) return suppressNotificationCount == 0; return false; } } public bool PropagateChildNotifications { get { return propagateChildNotifications; } set { propagateChildNotifications = value; } } public bool CanValidate { get; set; } public bool IsValid { get { if (CanValidate && validators != null) return !validators.Any((IDictionaryValidator v) => !v.IsValid(this)); return !CanValidate; } } public string Error { get { if (CanValidate && validators != null) return string.Join(Environment.NewLine, (from v in validators select v.Validate(this) into e where !string.IsNullOrEmpty(e) select e).ToArray()); return string.Empty; } } public string this[string columnName] { get { if (CanValidate && validators != null) { if (This.Properties.TryGetValue(columnName, out PropertyDescriptor property)) return string.Join(Environment.NewLine, (from v in validators select v.Validate(this, property) into e where !string.IsNullOrEmpty(e) select e).ToArray()); } return string.Empty; } } public IEnumerable<IDictionaryValidator> Validators => validators ?? Enumerable.Empty<IDictionaryValidator>(); public event PropertyChangingEventHandler PropertyChanging; public event PropertyChangedEventHandler PropertyChanged; public void CopyTo(IDictionaryAdapter other) { CopyTo(other, null); } public void CopyTo(IDictionaryAdapter other, Func<PropertyDescriptor, bool> selector) { if (!object.ReferenceEquals(this, other)) { if (!other.Meta.Type.IsAssignableFrom(Meta.Type)) throw new ArgumentException($"""{other.Meta.Type.FullName}""{Meta.Type.FullName}"""); if (!This.CopyStrategies.Aggregate(false, (bool copied, IDictionaryCopyStrategy s) => copied | s.Copy(this, other, ref selector))) { selector = (selector ?? ((Func<PropertyDescriptor, bool>)((PropertyDescriptor property) => true))); foreach (PropertyDescriptor item in from property in This.Properties.Values where selector(property) select property) { object value = GetProperty(item.PropertyName, true); other.SetProperty(item.PropertyName, ref value); } } } } public T Coerce<T>() where T : class { return (T)Coerce(typeof(T)); } public object Coerce(Type type) { if (type.IsAssignableFrom(Meta.Type)) return this; if (This.CoerceStrategy != null) { object obj = This.CoerceStrategy.Coerce(this, type); if (obj != null) return obj; } return This.Factory.GetAdapter(type, This.Dictionary, This.Descriptor); } public T Create<T>() { return Create<T>(new HybridDictionary()); } public object Create(Type type) { return Create(type, new HybridDictionary()); } public T Create<T>(IDictionary dictionary) { return (T)Create(typeof(T), dictionary ?? new HybridDictionary()); } public object Create(Type type, IDictionary dictionary) { if (This.CreateStrategy != null) { object obj = This.CreateStrategy.Create(this, type, dictionary); if (obj != null) return obj; } dictionary = (dictionary ?? new HybridDictionary()); return This.Factory.GetAdapter(type, dictionary, This.Descriptor); } public T Create<T>(Action<T> init) { return Create(new HybridDictionary(), init); } public T Create<T>(IDictionary dictionary, Action<T> init) { T val = Create<T>(dictionary ?? new HybridDictionary()); init?.Invoke(val); return val; } public DictionaryAdapterBase(DictionaryAdapterInstance instance) { This = instance; CanEdit = typeof(IEditableObject).IsAssignableFrom(Meta.Type); CanNotify = typeof(INotifyPropertyChanged).IsAssignableFrom(Meta.Type); CanValidate = typeof(IDataErrorInfo).IsAssignableFrom(Meta.Type); Initialize(); } public string GetKey(string propertyName) { if (This.Properties.TryGetValue(propertyName, out PropertyDescriptor value)) return value.GetKey(this, propertyName, This.Descriptor); return null; } public virtual object GetProperty(string propertyName, bool ifExists) { if (This.Properties.TryGetValue(propertyName, out PropertyDescriptor value)) { object propertyValue = value.GetPropertyValue(this, propertyName, null, This.Descriptor, ifExists); if (propertyValue is IEditableObject) AddEditDependency((IEditableObject)propertyValue); ComposeChildNotifications(value, null, propertyValue); return propertyValue; } return null; } public T GetPropertyOfType<T>(string propertyName) { object property = GetProperty(propertyName, false); if (property == null) return default(T); return (T)property; } public object ReadProperty(string key) { object propertyValue = null; if (!GetEditedProperty(key, out propertyValue)) { IDictionary dictionary = GetDictionary(This.Dictionary, ref key); if (dictionary != null) propertyValue = dictionary[key]; } return propertyValue; } public virtual bool SetProperty(string propertyName, ref object value) { bool flag = false; if (This.Properties.TryGetValue(propertyName, out PropertyDescriptor value2)) { if (!ShouldNotify) { flag = value2.SetPropertyValue(this, propertyName, ref value, This.Descriptor); Invalidate(); return flag; } object property = GetProperty(propertyName, true); if (!NotifyPropertyChanging(value2, property, value)) return false; TrackPropertyChangeScope trackPropertyChangeScope = TrackPropertyChange(value2, property, value); flag = value2.SetPropertyValue(this, propertyName, ref value, This.Descriptor); if (flag) trackPropertyChangeScope?.Notify(); } return flag; } public void StoreProperty(PropertyDescriptor property, string key, object value) { if (property == null || !EditProperty(property, key, value)) { IDictionary dictionary = GetDictionary(This.Dictionary, ref key); if (dictionary != null) dictionary[key] = value; } } public void ClearProperty(PropertyDescriptor property, string key) { key = (key ?? GetKey(property.PropertyName)); if (property == null || !ClearEditProperty(property, key)) GetDictionary(This.Dictionary, ref key)?.Remove(key); } public bool ShouldClearProperty(PropertyDescriptor property, object value) { if (property != null) return (from remove in property.Setters.OfType<RemoveIfAttribute>() where remove.ShouldRemove(value) select remove).Any(); return true; } public override bool Equals(object obj) { IDictionaryAdapter dictionaryAdapter = obj as IDictionaryAdapter; if (dictionaryAdapter == null) return false; if (object.ReferenceEquals(this, obj)) return true; if (Meta.Type != dictionaryAdapter.Meta.Type) return false; if (This.EqualityHashCodeStrategy != null) return This.EqualityHashCodeStrategy.Equals(this, dictionaryAdapter); return base.Equals(obj); } public override int GetHashCode() { if (This.OldHashCode.HasValue) return This.OldHashCode.Value; if (This.EqualityHashCodeStrategy == null || !This.EqualityHashCodeStrategy.GetHashCode(this, out int hashCode)) hashCode = base.GetHashCode(); This.OldHashCode = hashCode; return hashCode; } protected void Initialize() { IDictionaryInitializer[] initializers = This.Initializers; foreach (IDictionaryInitializer dictionaryInitializer in initializers) { dictionaryInitializer.Initialize(this, Meta.Behaviors); } foreach (PropertyDescriptor item in from p in This.Properties.Values where p.Fetch select p) { GetProperty(item.PropertyName, false); } } private static IDictionary GetDictionary(IDictionary dictionary, ref string key) { if (!key.StartsWith("!")) { string[] array = key.Split(new char[1] { ',' }); for (int i = 0; i < array.Length - 1; i++) { dictionary = (dictionary[array[i]] as IDictionary); if (dictionary == null) return null; } key = array[array.Length - 1]; } return dictionary; } public void BeginEdit() { if (CanEdit && (!IsEditing || SupportsMultiLevelEdit)) updates.Push(new Dictionary<string, Edit>()); } public void CancelEdit() { if (IsEditing) { if (editDependencies != null) { IEditableObject[] array = editDependencies.ToArray(); foreach (IEditableObject editableObject in array) { editableObject.CancelEdit(); } editDependencies.Clear(); } using (SuppressEditingBlock()) using (TrackReadonlyPropertyChanges()) { Dictionary<string, Edit> dictionary = updates.Peek(); if (dictionary.Count > 0) { foreach (Edit value in dictionary.Values) { Edit edit = value; edit.PropertyValue = GetProperty(edit.Property.PropertyName, true); } updates.Pop(); Edit[] array2 = dictionary.Values.ToArray(); for (int j = 0; j < array2.Length; j++) { Edit edit2 = array2[j]; object propertyValue = edit2.PropertyValue; object property = GetProperty(edit2.Property.PropertyName, true); if (!object.Equals(propertyValue, property)) { NotifyPropertyChanging(edit2.Property, propertyValue, property); NotifyPropertyChanged(edit2.Property, propertyValue, property); } } } } } } public void EndEdit() { if (IsEditing) { using (SuppressEditingBlock()) { Dictionary<string, Edit> dictionary = updates.Pop(); if (dictionary.Count > 0) { KeyValuePair<string, Edit>[] array = dictionary.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair<string, Edit> keyValuePair = array[i]; StoreProperty(null, keyValuePair.Key, keyValuePair.Value.PropertyValue); } } } if (editDependencies != null) { IEditableObject[] array2 = editDependencies.ToArray(); foreach (IEditableObject editableObject in array2) { editableObject.EndEdit(); } editDependencies.Clear(); } } } public void RejectChanges() { CancelEdit(); } public void AcceptChanges() { EndEdit(); } public IDisposable SuppressEditingBlock() { return new SuppressEditingScope(this); } public void SuppressEditing() { suppressEditingCount++; } public void ResumeEditing() { suppressEditingCount--; } protected bool GetEditedProperty(string propertyName, out object propertyValue) { if (updates != null) { Dictionary<string, Edit>[] array = updates.ToArray(); foreach (Dictionary<string, Edit> dictionary in array) { if (dictionary.TryGetValue(propertyName, out Edit value)) { propertyValue = value.PropertyValue; return true; } } } propertyValue = null; return false; } protected bool EditProperty(PropertyDescriptor property, string key, object propertyValue) { if (IsEditing) { updates.Peek()[key] = new Edit(property, propertyValue); return true; } return false; } protected bool ClearEditProperty(PropertyDescriptor property, string key) { if (IsEditing) { updates.Peek().Remove(key); return true; } return false; } protected void AddEditDependency(IEditableObject editDependency) { if (IsEditing) { if (editDependencies == null) editDependencies = new HashSet<IEditableObject>(); if (editDependencies.Add(editDependency)) editDependency.BeginEdit(); } } public IDisposable SuppressNotificationsBlock() { return new SuppressNotificationsScope(this); } public void SuppressNotifications() { suppressNotificationCount++; } public void ResumeNotifications() { suppressNotificationCount--; } protected bool NotifyPropertyChanging(PropertyDescriptor property, object oldValue, object newValue) { if (!property.SuppressNotifications) { PropertyChangingEventHandler propertyChanging = this.PropertyChanging; if (propertyChanging != null) { PropertyModifyingEventArgs propertyModifyingEventArgs = new PropertyModifyingEventArgs(property.PropertyName, oldValue, newValue); propertyChanging(this, propertyModifyingEventArgs); return !propertyModifyingEventArgs.Cancel; } } return true; } protected void NotifyPropertyChanged(PropertyDescriptor property, object oldValue, object newValue) { if (!property.SuppressNotifications) { PropertyChangedEventHandler propertyChanged = this.PropertyChanged; ComposeChildNotifications(property, oldValue, newValue); propertyChanged?.Invoke(this, new PropertyModifiedEventArgs(property.PropertyName, oldValue, newValue)); } } protected void NotifyPropertyChanged(string propertyName) { if (ShouldNotify) this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } protected TrackPropertyChangeScope TrackPropertyChange(PropertyDescriptor property, object oldValue, object newValue) { if (ShouldNotify && !property.SuppressNotifications) return new TrackPropertyChangeScope(this, property, oldValue); return null; } protected TrackPropertyChangeScope TrackReadonlyPropertyChanges() { if (ShouldNotify && readonlyTrackingScope == null) return readonlyTrackingScope = new TrackPropertyChangeScope(this); return null; } private void ComposeChildNotifications(PropertyDescriptor property, object oldValue, object newValue) { if (composedChildNotifications == null) composedChildNotifications = new Dictionary<object, object>(); if (oldValue != null && composedChildNotifications.TryGetValue(oldValue, out object value)) { composedChildNotifications.Remove(oldValue); if (oldValue is INotifyPropertyChanged) { ((INotifyPropertyChanged)oldValue).PropertyChanged -= Child_PropertyChanged; if (oldValue is INotifyPropertyChanging) ((INotifyPropertyChanging)oldValue).PropertyChanging -= Child_PropertyChanging; } else if (oldValue is IBindingList) { ((IBindingList)oldValue).ListChanged -= (ListChangedEventHandler)value; } } if (newValue != null && !composedChildNotifications.ContainsKey(newValue)) { if (newValue is INotifyPropertyChanged) { ((INotifyPropertyChanged)newValue).PropertyChanged += Child_PropertyChanged; if (newValue is INotifyPropertyChanging) ((INotifyPropertyChanging)newValue).PropertyChanging += Child_PropertyChanging; composedChildNotifications.Add(newValue, null); } else if (newValue is IBindingList) { ListChangedEventHandler value2 = delegate(object sender, ListChangedEventArgs args) { if (propagateChildNotifications) { PropertyChangedEventHandler propertyChanged = this.PropertyChanged; if (propertyChanged != null) { if (args.PropertyDescriptor != null) { string name = args.PropertyDescriptor.Name; propertyChanged(sender, new PropertyChangedEventArgs(name)); } propertyChanged(this, new PropertyChangedEventArgs(property.PropertyName)); } } }; ((IBindingList)newValue).ListChanged += value2; composedChildNotifications.Add(newValue, value2); } } } private void Child_PropertyChanging(object sender, PropertyChangingEventArgs e) { if (propagateChildNotifications) this.PropertyChanging?.Invoke(sender, e); } private void Child_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (propagateChildNotifications) this.PropertyChanged?.Invoke(sender, e); } public DictionaryValidateGroup ValidateGroups(params object[] groups) { return new DictionaryValidateGroup(groups, this); } public void AddValidator(IDictionaryValidator validator) { if (validators == null) validators = new HashSet<IDictionaryValidator>(); validators.Add(validator); } protected internal void Invalidate() { if (CanValidate) { if (validators != null) { foreach (IDictionaryValidator validator in validators) { validator.Invalidate(this); } } NotifyPropertyChanged("IsValid"); } } } }