397 lines
18 KiB
C#
397 lines
18 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Object used to represent results from multiple <see cref="RoboCommand"/>s. <br/>
|
|
/// As <see cref="RoboCopyResults"/> are added to this object, it will update the Totals and Averages accordingly.<para/>
|
|
/// Implements:
|
|
/// <br/><see cref="IRoboCopyResultsList"/>
|
|
/// <br/><see cref="IList{RoboCopyResults}"/> where T = RoboCopyResults
|
|
/// <br/><see cref="INotifyCollectionChanged"/>
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <see href="https://github.com/tjscience/RoboSharp/wiki/RoboCopyResultsList"/>
|
|
/// </remarks>
|
|
public sealed class RoboCopyResultsList : IRoboCopyResultsList, IList<RoboCopyResults>, INotifyCollectionChanged
|
|
{
|
|
#region < Constructors >
|
|
|
|
/// <inheritdoc cref="List{T}.List()"/>
|
|
public RoboCopyResultsList() { InitCollection(null); Init(); }
|
|
|
|
/// <param name="result">Populate the new List object with this result as the first item.</param>
|
|
/// <inheritdoc cref="List{T}.List(IEnumerable{T})"/>
|
|
public RoboCopyResultsList(RoboCopyResults result) { ResultsList.Add(result); InitCollection(null); Init(); }
|
|
|
|
/// <inheritdoc cref="List{T}.List(IEnumerable{T})"/>
|
|
public RoboCopyResultsList(IEnumerable<RoboCopyResults> collection) { InitCollection(collection); Init(); }
|
|
|
|
/// <summary>
|
|
/// Clone a RoboCopyResultsList into a new object
|
|
/// </summary>
|
|
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<RoboCopyResults> collection)
|
|
{
|
|
ResultsList.AddRange(collection);
|
|
ResultsList.CollectionChanged += OnCollectionChanged;
|
|
}
|
|
|
|
private void Init()
|
|
{
|
|
Total_DirStatsField = new Lazy<Statistic>(GetLazyFunc(GetDirectoriesStatistics, StatType.Directories));
|
|
Total_FileStatsField = new Lazy<Statistic>(GetLazyFunc(GetFilesStatistics, StatType.Files));
|
|
Total_ByteStatsField = new Lazy<Statistic>(GetLazyFunc(GetByteStatistics, StatType.Bytes));
|
|
Average_SpeedStatsField = new Lazy<AverageSpeedStatistic>(GetLazyAverageSpeedFunc());
|
|
ExitStatusSummaryField = new Lazy<RoboCopyCombinedExitStatus>(GetLazCombinedStatusFunc());
|
|
}
|
|
|
|
private Func<Statistic> GetLazyFunc(Func<IStatistic[]> Action, StatType StatType) => new Func<Statistic>(() => Statistic.AddStatistics(Action.Invoke(), StatType));
|
|
private Func<AverageSpeedStatistic> GetLazyAverageSpeedFunc() => new Func<AverageSpeedStatistic>(() => AverageSpeedStatistic.GetAverage(GetSpeedStatistics()));
|
|
private Func<RoboCopyCombinedExitStatus> GetLazCombinedStatusFunc() => new Func<RoboCopyCombinedExitStatus>(() => RoboCopyCombinedExitStatus.CombineStatuses(GetStatuses()));
|
|
private Lazy<T> GetLazyStat<T>(Lazy<T> lazyStat, Func<T> action) where T : ICloneable
|
|
{
|
|
if (lazyStat.IsValueCreated)
|
|
{
|
|
var clone = lazyStat.Value.Clone();
|
|
return new Lazy<T>(() => (T)clone);
|
|
}
|
|
else
|
|
{
|
|
return new Lazy<T>(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<Statistic> Total_DirStatsField;
|
|
private Lazy<Statistic> Total_ByteStatsField;
|
|
private Lazy<Statistic> Total_FileStatsField;
|
|
private Lazy<AverageSpeedStatistic> Average_SpeedStatsField;
|
|
private Lazy<RoboCopyCombinedExitStatus> ExitStatusSummaryField;
|
|
private readonly ObservableList<RoboCopyResults> ResultsList = new ObservableList<RoboCopyResults>();
|
|
|
|
#endregion
|
|
|
|
#region < Events >
|
|
|
|
/// <summary>
|
|
/// Delegate for objects to send notification that the list behind an <see cref="IRoboCopyResultsList"/> interface has been updated
|
|
/// </summary>
|
|
public delegate void ResultsListUpdated(object sender, ResultListUpdatedEventArgs e);
|
|
|
|
#endregion
|
|
|
|
#region < Public Properties >
|
|
|
|
/// <summary> Sum of all DirectoryStatistics objects </summary>
|
|
/// <remarks>Underlying value is Lazy{Statistic} object - Initial value not calculated until first request. </remarks>
|
|
public IStatistic DirectoriesStatistic => Total_DirStatsField?.Value;
|
|
|
|
/// <summary> Sum of all ByteStatistics objects </summary>
|
|
/// <remarks>Underlying value is Lazy{Statistic} object - Initial value not calculated until first request. </remarks>
|
|
public IStatistic BytesStatistic => Total_ByteStatsField?.Value;
|
|
|
|
/// <summary> Sum of all FileStatistics objects </summary>
|
|
/// <remarks>Underlying value is Lazy{Statistic} object - Initial value not calculated until first request. </remarks>
|
|
public IStatistic FilesStatistic => Total_FileStatsField?.Value;
|
|
|
|
/// <summary> Average of all SpeedStatistics objects </summary>
|
|
/// <remarks>Underlying value is Lazy{SpeedStatistic} object - Initial value not calculated until first request. </remarks>
|
|
public ISpeedStatistic SpeedStatistic => Average_SpeedStatsField?.Value;
|
|
|
|
/// <summary> Sum of all RoboCopyExitStatus objects </summary>
|
|
/// <remarks>Underlying value is Lazy object - Initial value not calculated until first request. </remarks>
|
|
public IRoboCopyCombinedExitStatus Status => ExitStatusSummaryField?.Value;
|
|
|
|
/// <summary> The Collection of RoboCopy Results. Add/Removal of <see cref="RoboCopyResults"/> objects must be performed through this object's methods, not on the list directly. </summary>
|
|
public IReadOnlyList<RoboCopyResults> Collection => ResultsList;
|
|
|
|
/// <inheritdoc cref="List{T}.Count"/>
|
|
public int Count => ResultsList.Count;
|
|
|
|
/// <summary>
|
|
/// Get or Set the element at the specified index.
|
|
/// </summary>
|
|
/// <param name="index">The zero-based index of the item to Get or Set.</param>
|
|
/// <exception cref="ArgumentOutOfRangeException"/>
|
|
public RoboCopyResults this[int index] { get => ResultsList[index]; set => ResultsList[index] = value; }
|
|
|
|
#endregion
|
|
|
|
#region < Get Array Methods ( Public ) >
|
|
|
|
/// <summary>
|
|
/// Get a snapshot of the ByteStatistics objects from this list.
|
|
/// </summary>
|
|
/// <returns>New array of the ByteStatistic objects</returns>
|
|
public IStatistic[] GetByteStatistics()
|
|
{
|
|
List<Statistic> tmp = new List<Statistic> { };
|
|
foreach (RoboCopyResults r in this)
|
|
tmp.Add(r?.BytesStatistic);
|
|
return tmp.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a snapshot of the DirectoriesStatistic objects from this list.
|
|
/// </summary>
|
|
/// <returns>New array of the DirectoriesStatistic objects</returns>
|
|
public IStatistic[] GetDirectoriesStatistics()
|
|
{
|
|
List<Statistic> tmp = new List<Statistic> { };
|
|
foreach (RoboCopyResults r in this)
|
|
tmp.Add(r?.DirectoriesStatistic);
|
|
return tmp.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a snapshot of the FilesStatistic objects from this list.
|
|
/// </summary>
|
|
/// <returns>New array of the FilesStatistic objects</returns>
|
|
public IStatistic[] GetFilesStatistics()
|
|
{
|
|
List<Statistic> tmp = new List<Statistic> { };
|
|
foreach (RoboCopyResults r in this)
|
|
tmp.Add(r?.FilesStatistic);
|
|
return tmp.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a snapshot of the FilesStatistic objects from this list.
|
|
/// </summary>
|
|
/// <returns>New array of the FilesStatistic objects</returns>
|
|
public RoboCopyExitStatus[] GetStatuses()
|
|
{
|
|
List<RoboCopyExitStatus> tmp = new List<RoboCopyExitStatus> { };
|
|
foreach (RoboCopyResults r in this)
|
|
tmp.Add(r?.Status);
|
|
return tmp.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a snapshot of the FilesStatistic objects from this list.
|
|
/// </summary>
|
|
/// <returns>New array of the FilesStatistic objects</returns>
|
|
public ISpeedStatistic[] GetSpeedStatistics()
|
|
{
|
|
List<SpeedStatistic> tmp = new List<SpeedStatistic> { };
|
|
foreach (RoboCopyResults r in this)
|
|
tmp.Add(r?.SpeedStatistic);
|
|
return tmp.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Combine the <see cref="RoboCopyResults.RoboCopyErrors"/> into a single array of errors
|
|
/// </summary>
|
|
/// <returns>New array of the ErrorEventArgs objects</returns>
|
|
public ErrorEventArgs[] GetErrors()
|
|
{
|
|
List<ErrorEventArgs> tmp = new List<ErrorEventArgs> { };
|
|
foreach (RoboCopyResults r in this)
|
|
tmp.AddRange(r?.RoboCopyErrors);
|
|
return tmp.ToArray();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region < INotifyCollectionChanged >
|
|
|
|
/// <summary>
|
|
/// <inheritdoc cref="ObservableList{T}.CollectionChanged"/>
|
|
/// </summary>
|
|
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
|
|
|
/// <summary>Process the Added/Removed items, then fire the event</summary>
|
|
/// <inheritdoc cref="ObservableList{T}.OnCollectionChanged(NotifyCollectionChangedEventArgs)"/>
|
|
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 >
|
|
|
|
/// <summary> Clone this object to a new RoboCopyResultsList </summary>
|
|
public RoboCopyResultsList Clone() => new RoboCopyResultsList(this);
|
|
|
|
#endregion
|
|
|
|
#region < IList{T} Implementation >
|
|
|
|
bool ICollection<RoboCopyResults>.IsReadOnly => false;
|
|
|
|
/// <inheritdoc cref="IList{T}.IndexOf(T)"/>
|
|
public int IndexOf(RoboCopyResults item) => ResultsList.IndexOf(item);
|
|
|
|
/// <inheritdoc cref="ObservableList{T}.Insert(int, T)"/>
|
|
public void Insert(int index, RoboCopyResults item) => ResultsList.Insert(index, item);
|
|
|
|
/// <inheritdoc cref="ObservableList{T}.RemoveAt(int)"/>
|
|
public void RemoveAt(int index) => ResultsList.RemoveAt(index);
|
|
|
|
/// <inheritdoc cref="ObservableList{T}.Add(T)"/>
|
|
public void Add(RoboCopyResults item) => ResultsList.Add(item);
|
|
|
|
/// <inheritdoc cref="ObservableList{T}.Clear"/>
|
|
public void Clear() => ResultsList.Clear();
|
|
|
|
/// <inheritdoc cref="IList.Contains(object)"/>
|
|
public bool Contains(RoboCopyResults item) => ResultsList.Contains(item);
|
|
|
|
/// <inheritdoc cref="ICollection.CopyTo(Array, int)"/>
|
|
public void CopyTo(RoboCopyResults[] array, int arrayIndex) => ResultsList.CopyTo(array, arrayIndex);
|
|
|
|
/// <inheritdoc cref="ObservableList{T}.Remove(T)"/>
|
|
public bool Remove(RoboCopyResults item) => ResultsList.Remove(item);
|
|
|
|
/// <inheritdoc cref="List{T}.GetEnumerator"/>
|
|
public IEnumerator<RoboCopyResults> GetEnumerator() => ResultsList.GetEnumerator();
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() => ResultsList.GetEnumerator();
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
}
|
|
|