using System; using System.Collections.Generic; //using System.Linq; using System.Text; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using RoboSharp.EventArgObjects; using RoboSharp.Interfaces; using StatType = RoboSharp.Results.Statistic.StatType; using System.Collections; namespace RoboSharp.Results { /// /// Object used to represent results from multiple s.
/// As are added to this object, it will update the Totals and Averages accordingly. /// Implements: ///
///
where T = RoboCopyResults ///
///
/// /// /// public sealed class RoboCopyResultsList : IRoboCopyResultsList, IList, INotifyCollectionChanged { #region < Constructors > /// public RoboCopyResultsList() { InitCollection(null); Init(); } /// Populate the new List object with this result as the first item. /// public RoboCopyResultsList(RoboCopyResults result) { ResultsList.Add(result); InitCollection(null); Init(); } /// public RoboCopyResultsList(IEnumerable collection) { InitCollection(collection); Init(); } /// /// Clone a RoboCopyResultsList into a new object /// public RoboCopyResultsList(RoboCopyResultsList resultsList) { InitCollection(resultsList); Total_DirStatsField = GetLazyStat(resultsList.Total_DirStatsField, GetLazyFunc(GetDirectoriesStatistics, StatType.Directories)); Total_FileStatsField = GetLazyStat(resultsList.Total_FileStatsField, GetLazyFunc(GetFilesStatistics, StatType.Files)); Total_ByteStatsField = GetLazyStat(resultsList.Total_ByteStatsField, GetLazyFunc(GetByteStatistics, StatType.Bytes)); Average_SpeedStatsField = GetLazyStat(resultsList.Average_SpeedStatsField, GetLazyAverageSpeedFunc()); ExitStatusSummaryField = GetLazyStat(resultsList.ExitStatusSummaryField, GetLazCombinedStatusFunc()); } #region < Constructor Helper Methods > private void InitCollection(IEnumerable collection) { ResultsList.AddRange(collection); ResultsList.CollectionChanged += OnCollectionChanged; } private void Init() { Total_DirStatsField = new Lazy(GetLazyFunc(GetDirectoriesStatistics, StatType.Directories)); Total_FileStatsField = new Lazy(GetLazyFunc(GetFilesStatistics, StatType.Files)); Total_ByteStatsField = new Lazy(GetLazyFunc(GetByteStatistics, StatType.Bytes)); Average_SpeedStatsField = new Lazy(GetLazyAverageSpeedFunc()); ExitStatusSummaryField = new Lazy(GetLazCombinedStatusFunc()); } private Func GetLazyFunc(Func Action, StatType StatType) => new Func(() => Statistic.AddStatistics(Action.Invoke(), StatType)); private Func GetLazyAverageSpeedFunc() => new Func(() => AverageSpeedStatistic.GetAverage(GetSpeedStatistics())); private Func GetLazCombinedStatusFunc() => new Func(() => RoboCopyCombinedExitStatus.CombineStatuses(GetStatuses())); private Lazy GetLazyStat(Lazy lazyStat, Func action) where T : ICloneable { if (lazyStat.IsValueCreated) { var clone = lazyStat.Value.Clone(); return new Lazy(() => (T)clone); } else { return new Lazy(action); } } #endregion #endregion #region < Fields > //These objects are the underlying Objects that may be bound to by consumers. //The values are updated upon request of the associated property. //This is so that properties are not returning new objects every request (which would break bindings) //If the statistic is never requested, then Lazy<> allows the list to skip performing the math against that statistic. private Lazy Total_DirStatsField; private Lazy Total_ByteStatsField; private Lazy Total_FileStatsField; private Lazy Average_SpeedStatsField; private Lazy ExitStatusSummaryField; private readonly ObservableList ResultsList = new ObservableList(); #endregion #region < Events > /// /// Delegate for objects to send notification that the list behind an interface has been updated /// public delegate void ResultsListUpdated(object sender, ResultListUpdatedEventArgs e); #endregion #region < Public Properties > /// Sum of all DirectoryStatistics objects /// Underlying value is Lazy{Statistic} object - Initial value not calculated until first request. public IStatistic DirectoriesStatistic => Total_DirStatsField?.Value; /// Sum of all ByteStatistics objects /// Underlying value is Lazy{Statistic} object - Initial value not calculated until first request. public IStatistic BytesStatistic => Total_ByteStatsField?.Value; /// Sum of all FileStatistics objects /// Underlying value is Lazy{Statistic} object - Initial value not calculated until first request. public IStatistic FilesStatistic => Total_FileStatsField?.Value; /// Average of all SpeedStatistics objects /// Underlying value is Lazy{SpeedStatistic} object - Initial value not calculated until first request. public ISpeedStatistic SpeedStatistic => Average_SpeedStatsField?.Value; /// Sum of all RoboCopyExitStatus objects /// Underlying value is Lazy object - Initial value not calculated until first request. public IRoboCopyCombinedExitStatus Status => ExitStatusSummaryField?.Value; /// The Collection of RoboCopy Results. Add/Removal of objects must be performed through this object's methods, not on the list directly. public IReadOnlyList Collection => ResultsList; /// public int Count => ResultsList.Count; /// /// Get or Set the element at the specified index. /// /// The zero-based index of the item to Get or Set. /// public RoboCopyResults this[int index] { get => ResultsList[index]; set => ResultsList[index] = value; } #endregion #region < Get Array Methods ( Public ) > /// /// Get a snapshot of the ByteStatistics objects from this list. /// /// New array of the ByteStatistic objects public IStatistic[] GetByteStatistics() { List tmp = new List { }; foreach (RoboCopyResults r in this) tmp.Add(r?.BytesStatistic); return tmp.ToArray(); } /// /// Get a snapshot of the DirectoriesStatistic objects from this list. /// /// New array of the DirectoriesStatistic objects public IStatistic[] GetDirectoriesStatistics() { List tmp = new List { }; foreach (RoboCopyResults r in this) tmp.Add(r?.DirectoriesStatistic); return tmp.ToArray(); } /// /// Get a snapshot of the FilesStatistic objects from this list. /// /// New array of the FilesStatistic objects public IStatistic[] GetFilesStatistics() { List tmp = new List { }; foreach (RoboCopyResults r in this) tmp.Add(r?.FilesStatistic); return tmp.ToArray(); } /// /// Get a snapshot of the FilesStatistic objects from this list. /// /// New array of the FilesStatistic objects public RoboCopyExitStatus[] GetStatuses() { List tmp = new List { }; foreach (RoboCopyResults r in this) tmp.Add(r?.Status); return tmp.ToArray(); } /// /// Get a snapshot of the FilesStatistic objects from this list. /// /// New array of the FilesStatistic objects public ISpeedStatistic[] GetSpeedStatistics() { List tmp = new List { }; foreach (RoboCopyResults r in this) tmp.Add(r?.SpeedStatistic); return tmp.ToArray(); } /// /// Combine the into a single array of errors /// /// New array of the ErrorEventArgs objects public ErrorEventArgs[] GetErrors() { List tmp = new List { }; foreach (RoboCopyResults r in this) tmp.AddRange(r?.RoboCopyErrors); return tmp.ToArray(); } #endregion #region < INotifyCollectionChanged > /// /// /// public event NotifyCollectionChangedEventHandler CollectionChanged; /// Process the Added/Removed items, then fire the event /// private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Move) goto RaiseEvent; // Sorting causes no change in math -> Simply raise the event //Reset means a drastic change -> Recalculate everything, then goto RaiseEvent if (e.Action == NotifyCollectionChangedAction.Reset) { //Bytes if (Total_ByteStatsField.IsValueCreated) { Total_ByteStatsField.Value.Reset(false); Total_ByteStatsField.Value.AddStatistic(GetByteStatistics()); } //Directories if (Total_DirStatsField.IsValueCreated) { Total_DirStatsField.Value.Reset(false); Total_DirStatsField.Value.AddStatistic(GetDirectoriesStatistics()); } //Files if (Total_FileStatsField.IsValueCreated) { Total_FileStatsField.Value.Reset(false); Total_FileStatsField.Value.AddStatistic(GetFilesStatistics()); } //Exit Status if (ExitStatusSummaryField.IsValueCreated) { ExitStatusSummaryField.Value.Reset(false); ExitStatusSummaryField.Value.CombineStatus(GetStatuses()); } //Speed if (Average_SpeedStatsField.IsValueCreated) { Average_SpeedStatsField.Value.Reset(false); Average_SpeedStatsField.Value.Average(GetSpeedStatistics()); } goto RaiseEvent; } //Process New Items if (e.NewItems != null) { int i = 0; int i2 = e.NewItems.Count; foreach (RoboCopyResults r in e?.NewItems) { i++; bool RaiseValueChangeEvent = (e.OldItems == null || e.OldItems.Count == 0) && (i == i2); //Prevent raising the event if more calculation needs to be performed either from NewItems or from OldItems //Bytes if (Total_ByteStatsField.IsValueCreated) Total_ByteStatsField.Value.AddStatistic(r?.BytesStatistic, RaiseValueChangeEvent); //Directories if (Total_DirStatsField.IsValueCreated) Total_DirStatsField.Value.AddStatistic(r?.DirectoriesStatistic, RaiseValueChangeEvent); //Files if (Total_FileStatsField.IsValueCreated) Total_FileStatsField.Value.AddStatistic(r?.FilesStatistic, RaiseValueChangeEvent); //Exit Status if (ExitStatusSummaryField.IsValueCreated) ExitStatusSummaryField.Value.CombineStatus(r?.Status, RaiseValueChangeEvent); //Speed if (Average_SpeedStatsField.IsValueCreated) { Average_SpeedStatsField.Value.Add(r?.SpeedStatistic); if (RaiseValueChangeEvent) Average_SpeedStatsField.Value.CalculateAverage(); } } } //Process Removed Items if (e.OldItems != null) { int i = 0; int i2 = e.OldItems.Count; foreach (RoboCopyResults r in e?.OldItems) { i++; bool RaiseValueChangeEvent = i == i2; //Bytes if (Total_ByteStatsField.IsValueCreated) Total_ByteStatsField.Value.Subtract(r?.BytesStatistic, RaiseValueChangeEvent); //Directories if (Total_DirStatsField.IsValueCreated) Total_DirStatsField.Value.Subtract(r?.DirectoriesStatistic, RaiseValueChangeEvent); //Files if (Total_FileStatsField.IsValueCreated) Total_FileStatsField.Value.Subtract(r?.FilesStatistic, RaiseValueChangeEvent); //Exit Status if (ExitStatusSummaryField.IsValueCreated && RaiseValueChangeEvent) { ExitStatusSummaryField.Value.Reset(false); ExitStatusSummaryField.Value.CombineStatus(GetStatuses()); } //Speed if (Average_SpeedStatsField.IsValueCreated) { if (this.Count == 0) Average_SpeedStatsField.Value.Reset(); else Average_SpeedStatsField.Value.Subtract(r.SpeedStatistic); if (RaiseValueChangeEvent) Average_SpeedStatsField.Value.CalculateAverage(); } } } RaiseEvent: //Raise the CollectionChanged event CollectionChanged?.Invoke(this, e); } #endregion #region < ICloneable > /// Clone this object to a new RoboCopyResultsList public RoboCopyResultsList Clone() => new RoboCopyResultsList(this); #endregion #region < IList{T} Implementation > bool ICollection.IsReadOnly => false; /// public int IndexOf(RoboCopyResults item) => ResultsList.IndexOf(item); /// public void Insert(int index, RoboCopyResults item) => ResultsList.Insert(index, item); /// public void RemoveAt(int index) => ResultsList.RemoveAt(index); /// public void Add(RoboCopyResults item) => ResultsList.Add(item); /// public void Clear() => ResultsList.Clear(); /// public bool Contains(RoboCopyResults item) => ResultsList.Contains(item); /// public void CopyTo(RoboCopyResults[] array, int arrayIndex) => ResultsList.CopyTo(array, arrayIndex); /// public bool Remove(RoboCopyResults item) => ResultsList.Remove(item); /// public IEnumerator GetEnumerator() => ResultsList.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => ResultsList.GetEnumerator(); #endregion } }