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

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 : IDictionaryCreate, IDictionaryAdapter, IDictionaryEdit, IEditableObject, IRevertibleChangeTracking, IChangeTracking, IDictionaryNotify, INotifyPropertyChanging, INotifyPropertyChanged, 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 NotificationSuppressionScope : IDisposable { private readonly DictionaryAdapterBase adapter; public NotificationSuppressionScope(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 Dictionary<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 void Dispose() { Notify(); } public bool Notify() { if (readOnlyTrackingScope == this) { readOnlyTrackingScope = null; return NotifyReadonly(); } object effectivePropertyValue = GetEffectivePropertyValue(property); if (!NotifyIfChanged(property, existingValue, effectivePropertyValue)) return false; if (readOnlyTrackingScope == null) NotifyReadonly(); return true; } 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)) return false; adapter.NotifyPropertyChanged(descriptor, oldValue, newValue); return true; } private object GetEffectivePropertyValue(PropertyDescriptor property) { object obj = adapter.GetProperty(property.PropertyName, true); if (obj == null || !property.IsDynamicProperty) return obj; IDynamicValue dynamicValue = obj as IDynamicValue; if (dynamicValue == null) return obj; return dynamicValue.GetValue(); } } private int suppressEditingCount; private Stack<Dictionary<string, Edit>> updates; private HashSet<IEditableObject> editDependencies; [ThreadStatic] private static TrackPropertyChangeScope readOnlyTrackingScope; private int suppressNotificationCount; 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 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 { get { IEnumerable<IDictionaryValidator> enumerable = validators; return enumerable ?? Enumerable.Empty<IDictionaryValidator>(); } } public event PropertyChangingEventHandler PropertyChanging; public event PropertyChangedEventHandler PropertyChanged; 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 void CopyTo(IDictionaryAdapter other) { CopyTo(other, null); } public void CopyTo(IDictionaryAdapter other, Func<PropertyDescriptor, bool> selector) { if (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 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); 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 (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() { object[] behaviors = Meta.Behaviors; IDictionaryInitializer[] initializers = This.Initializers; for (int i = 0; i < initializers.Length; i++) { initializers[i].Initialize(this, behaviors); } foreach (PropertyDescriptor value in This.Properties.Values) { if (value.Fetch) GetProperty(value.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(); for (int i = 0; i < array.Length; i++) { array[i].CancelEdit(); } editDependencies.Clear(); } using (SuppressEditingBlock()) using (TrackReadonlyPropertyChanges()) { Dictionary<string, Edit> dictionary = updates.Peek(); if (dictionary.Count > 0) { foreach (Edit value in dictionary.Values) { Edit current = value; current.PropertyValue = GetProperty(current.Property.PropertyName, true); } updates.Pop(); Edit[] array2 = dictionary.Values.ToArray(); foreach (Edit edit in array2) { object propertyValue = edit.PropertyValue; object property = GetProperty(edit.Property.PropertyName, true); if (!object.Equals(propertyValue, property)) { NotifyPropertyChanging(edit.Property, propertyValue, property); NotifyPropertyChanged(edit.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(); for (int i = 0; i < array2.Length; i++) { array2[i].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(); for (int i = 0; i < array.Length; i++) { if (array[i].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 NotificationSuppressionScope(this); } public void SuppressNotifications() { suppressNotificationCount++; } public void ResumeNotifications() { suppressNotificationCount--; } protected bool NotifyPropertyChanging(PropertyDescriptor property, object oldValue, object newValue) { if (property.SuppressNotifications) return true; PropertyChangingEventHandler propertyChanging = this.PropertyChanging; if (propertyChanging == null) return true; PropertyChangingEventArgsEx propertyChangingEventArgsEx = new PropertyChangingEventArgsEx(property.PropertyName, oldValue, newValue); propertyChanging(this, propertyChangingEventArgsEx); return !propertyChangingEventArgsEx.Cancel; } protected void NotifyPropertyChanged(PropertyDescriptor property, object oldValue, object newValue) { if (!property.SuppressNotifications) this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgsEx(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 null; return new TrackPropertyChangeScope(this, property, oldValue); } protected TrackPropertyChangeScope TrackReadonlyPropertyChanges() { if (!ShouldNotify || readOnlyTrackingScope != null) return null; return readOnlyTrackingScope = new TrackPropertyChangeScope(this); } 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"); } } } }