/// <summary> /// Initializes a new instance of the <see cref="DataStructureCellProtocols{TValue}"/> class. /// </summary> /// <param name="reportCache">The report cache in-context.</param> /// <param name="protocolFactory">The protocol factory to use when executing an <see cref="IOperationOutputCell{TValue}"/>'s <see cref="IOperationOutputCell{TValue}.Operation"/>.</param> /// <param name="timestampUtc">The timestamp (in UTC) to use when recording a <see cref="CellOpExecutionEventBase"/> with an <see cref="IOperationOutputCell{TValue}"/>.</param> /// <param name="getRecalcPhaseFunc">Func that gets the <see cref="RecalcPhase"/>.</param> public DataStructureCellProtocols( ReportCache reportCache, IProtocolFactory protocolFactory, DateTime timestampUtc, Func <RecalcPhase> getRecalcPhaseFunc) { if (reportCache == null) { throw new ArgumentNullException(nameof(reportCache)); } // ReSharper disable once JoinNullCheckWithUsage if (protocolFactory == null) { throw new ArgumentNullException(nameof(protocolFactory)); } if (timestampUtc.Kind != DateTimeKind.Utc) { throw new ArgumentException(Invariant($"{nameof(timestampUtc)} is not in UTC time.")); } if (getRecalcPhaseFunc == null) { throw new ArgumentNullException(nameof(getRecalcPhaseFunc)); } this.reportCache = reportCache; this.protocolFactory = protocolFactory; this.timestampUtc = timestampUtc; this.getRecalcPhaseFunc = getRecalcPhaseFunc; }
private static void ClearCells( this ReportCache reportCache, DateTime timestampUtc) { var details = Invariant($"Value cleared by {nameof(ReportExtensions)}.{nameof(Recalc)} or async overload."); foreach (var operationCell in reportCache.OperationCells) { operationCell.ClearCellValue(timestampUtc, details); } details = Invariant($"Validation cleared by {nameof(ReportExtensions)}.{nameof(Recalc)} or async overload."); foreach (var cell in reportCache.ValidationCells) { cell.ClearValidation(timestampUtc, details); } details = Invariant($"Availability check cleared by {nameof(ReportExtensions)}.{nameof(Recalc)} or async overload."); foreach (var cell in reportCache.AvailabilityCheckCells) { cell.ClearAvailabilityCheck(timestampUtc, details); } }
private static ChainOfResponsibilityProtocolFactory BuildProtocolFactoryToExecuteAllOperations( this ReportCache reportCache, DateTime timestampUtc, IReadOnlyCollection <Func <IProtocolFactory, IProtocolFactory> > protocolFactoryFuncs, IReadOnlyCollection <Type> additionalTypesForCoreCellOps, Func <RecalcPhase> getRecalcPhaseFunc) { protocolFactoryFuncs = protocolFactoryFuncs ?? new List <Func <IProtocolFactory, IProtocolFactory> >(); if (protocolFactoryFuncs.Any(_ => _ == null)) { throw new ArgumentException(Invariant($"{nameof(protocolFactoryFuncs)} contains a null element.")); } additionalTypesForCoreCellOps = additionalTypesForCoreCellOps ?? new List <Type>(); if (additionalTypesForCoreCellOps.Any(_ => _ == null)) { throw new ArgumentException(Invariant($"{nameof(additionalTypesForCoreCellOps)} contains a null element.")); } var result = new ChainOfResponsibilityProtocolFactory(); // Add caller's protocols to the chain of responsibility. foreach (var protocolFactoryFunc in protocolFactoryFuncs) { result.AddToEndOfChain(protocolFactoryFunc(result)); } // Add DataStructureCellProtocols{TValue} and DataStructureConvenienceProtocols{TResult} to chain of responsibility. var typesForCoreCellOps = DefaultTypesSupportedForCoreCellOps .Concat(additionalTypesForCoreCellOps) .ToList(); var coreProtocolsFactory = new ProtocolFactory(); ConstructorInfo GetCellProtocolsFunc(Type type) => typeof(DataStructureCellProtocols <>).MakeGenericType(type).GetConstructors().Single(); ConstructorInfo GetConvenienceProtocolsFunc(Type type) => typeof(DataStructureConvenienceProtocols <>).MakeGenericType(type).GetConstructors().Single(); var cellProtocolsConstructorInfoParams = new object[] { reportCache, result, timestampUtc, getRecalcPhaseFunc }; var convenienceProtocolsConstructorInfoParams = new object[] { result }; foreach (var typeForCoreCellOps in typesForCoreCellOps) { RegisterProtocols(typeForCoreCellOps, CachedTypeToCellProtocolsConstructorInfoMap, coreProtocolsFactory, GetCellProtocolsFunc, cellProtocolsConstructorInfoParams); RegisterProtocols(typeForCoreCellOps, CachedTypeToConvenienceProtocolsConstructorInfoMap, coreProtocolsFactory, GetConvenienceProtocolsFunc, convenienceProtocolsConstructorInfoParams); } result.AddToEndOfChain(coreProtocolsFactory); return(result); }
private static async Task ReCalcInternalAsync( this Report report, DateTime timestampUtc, IReadOnlyCollection <Func <IProtocolFactory, IProtocolFactory> > protocolFactoryFuncs = null, IReadOnlyCollection <Type> additionalTypesForCoreCellOps = null) { // NOTE: THIS CODE IS A NEAR DUPLICATE OF THE SYNC METHOD ABOVE; NO GOOD WAY TO D.R.Y. IT OUT var recalcPhase = RecalcPhase.Unknown; // ReSharper disable once AccessToModifiedClosure RecalcPhase GetRecalcPhaseFunc() => recalcPhase; var reportCache = new ReportCache(report); var protocolFactory = reportCache.BuildProtocolFactoryToExecuteAllOperations(timestampUtc, protocolFactoryFuncs, additionalTypesForCoreCellOps, GetRecalcPhaseFunc); reportCache.ClearCells(timestampUtc); recalcPhase = RecalcPhase.CellOpExecution; foreach (var cell in reportCache.OperationCells) { var executeOperationCellIfNecessaryOp = cell.BuildExecuteOperationCellIfNecessaryOp(); await protocolFactory.GetProtocolAndExecuteViaReflectionAsync(executeOperationCellIfNecessaryOp); } recalcPhase = RecalcPhase.Validation; foreach (var cell in reportCache.ValidationCells) { var validateCellIfNecessaryOp = new ValidateCellIfNecessaryOp(cell); await protocolFactory.GetProtocolAndExecuteViaReflectionAsync(validateCellIfNecessaryOp); } recalcPhase = RecalcPhase.AvailabilityCheck; foreach (var cell in reportCache.AvailabilityCheckCells) { var checkAvailabilityOfCellIfNecessaryOp = new CheckAvailabilityOfCellIfNecessaryOp(cell); await protocolFactory.GetProtocolAndExecuteViaReflectionAsync(checkAvailabilityOfCellIfNecessaryOp); } if (reportCache.PrepareToRerunRecalc(timestampUtc)) { await report.ReCalcInternalAsync(timestampUtc, protocolFactoryFuncs, additionalTypesForCoreCellOps); } }
private static bool PrepareToRerunRecalc( this ReportCache reportCache, DateTime timestampUtc) { var validityIsUnknown = reportCache.ValidationCells.Any(_ => _.GetValidity() == Validity.Unknown); var availabilityIsUnknown = reportCache.AvailabilityCheckCells.Any(_ => _.GetAvailability() == Availability.Unknown); bool result; if (validityIsUnknown || availabilityIsUnknown) { result = false; } else { var disabledInputCellsWithValues = reportCache .InputCells .Where(_ => _.GetAvailability() == Availability.Disabled) .Where(_ => _.HasCellValue()) .ToList(); if (disabledInputCellsWithValues.Any()) { foreach (var disabledInputCellWithValue in disabledInputCellsWithValues) { disabledInputCellWithValue.ClearCellValue(timestampUtc, Invariant($"{nameof(ReportExtensions)}.{nameof(Recalc)} found this disabled input cell having a value. Clearing the value and will re-run recalc.")); } result = true; } else { result = false; } } return(result); }