using System.Collections.Generic; using System.Linq; namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers { /// A watcher which detects changes to a collection of values using a specified instance. /// The value type within the collection. internal class ComparableListWatcher : BaseDisposableWatcher, ICollectionWatcher { /********* ** Fields *********/ /// The collection to watch. private readonly ICollection CurrentValues; /// The values during the previous update. private HashSet LastValues; /// The pairs added since the last reset. private readonly List AddedImpl = new(); /// The pairs removed since the last reset. private readonly List RemovedImpl = new(); /********* ** Accessors *********/ /// Whether the value changed since the last reset. public bool IsChanged => this.AddedImpl.Count > 0 || this.RemovedImpl.Count > 0; /// The values added since the last reset. public IEnumerable Added => this.AddedImpl; /// The values removed since the last reset. public IEnumerable Removed => this.RemovedImpl; /********* ** Public methods *********/ /// Construct an instance. /// The collection to watch. /// The equality comparer which indicates whether two values are the same. public ComparableListWatcher(ICollection values, IEqualityComparer comparer) { this.CurrentValues = values; this.LastValues = new HashSet(comparer); } /// Update the current value if needed. public void Update() { this.AssertNotDisposed(); // optimize for zero items if (this.CurrentValues.Count == 0) { if (this.LastValues.Count > 0) { this.AddedImpl.AddRange(this.LastValues); this.LastValues.Clear(); } return; } // detect changes HashSet curValues = new HashSet(this.CurrentValues, this.LastValues.Comparer); this.RemovedImpl.AddRange(from value in this.LastValues where !curValues.Contains(value) select value); this.AddedImpl.AddRange(from value in curValues where !this.LastValues.Contains(value) select value); this.LastValues = curValues; } /// Set the current value as the baseline. public void Reset() { this.AssertNotDisposed(); this.AddedImpl.Clear(); this.RemovedImpl.Clear(); } } }