This commit is contained in:
2025-10-27 17:39:18 -04:00
commit 31f723bea4
1579 changed files with 642409 additions and 0 deletions

View File

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