using System.Collections.ObjectModel; namespace Prefab.Base; /// /// Provides a customizable attribute bag with change tracking for Prefab domain objects. /// public class GenericAttributes : ICanBeCustomized { private readonly Dictionary _attributes = new(); private readonly List _changes = new(); /// IReadOnlyDictionary IHasGenericAttributes.GenericAttributes => new ReadOnlyDictionary(_attributes); /// /// Gets the ordered list of attribute mutations recorded since the last call to . /// public IReadOnlyList Changes => _changes.AsReadOnly(); /// /// Provides an extension point for enforcing key/value requirements before persisting the attribute. /// /// The attribute key being modified. /// The proposed attribute value. protected virtual void ValidateAttribute(string key, object value) { // Default: no validation. Override in derived classes for custom logic. } /// /// Sets or replaces an attribute value and records the mutation. /// /// The attribute key to set. /// The attribute value. public void SetAttribute(string key, object value) { _attributes.TryGetValue(key, out var oldValue); ValidateAttribute(key, value); _attributes[key] = value; _changes.Add(new GenericAttributeChange(key, oldValue, value)); } /// /// Removes an attribute if present and records the removal. /// /// The attribute key to remove. /// true when the key existed and was removed; otherwise false. public bool RemoveAttribute(string key) { if (_attributes.TryGetValue(key, out var oldValue) && _attributes.Remove(key)) { _changes.Add(new GenericAttributeChange(key, oldValue, null)); return true; } return false; } /// /// Attempts to read an attribute value and cast it to the requested type. /// /// The attribute key to read. /// Receives the typed attribute value when successful. /// The expected attribute type. /// true when the attribute exists and matches the requested type. public bool TryGetAttribute(string key, out T value) { if (_attributes.TryGetValue(key, out var obj) && obj is T t) { value = t; return true; } value = default!; return false; } /// /// Creates an immutable snapshot copy of the current attributes. /// public IReadOnlyDictionary Snapshot() => new ReadOnlyDictionary(new Dictionary(_attributes)); /// /// Clears all recorded attribute changes while keeping the current attribute values intact. /// public void ClearChanges() => _changes.Clear(); }