< Summary

Class:ReactiveUI.Validation.Contexts.ValidationContext
Assembly:ReactiveUI.Validation
File(s):D:\a\1\s\src\ReactiveUI.Validation\Contexts\ValidationContext.cs
Covered lines:57
Uncovered lines:6
Coverable lines:63
Total lines:197
Line coverage:90.4% (57 of 63)
Covered branches:6
Total branches:12
Branch coverage:50% (6 of 12)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.ctor(...)-100%50%
get_Valid()-100%100%
get_Validations()-100%100%
get_IsValid()-100%100%
get_ValidationStatusChange()-100%100%
get_Text()-100%100%
Add(...)-100%100%
GetIsValid()-100%50%
Dispose()-0%100%
Dispose(...)-0%0%
Activate()-100%100%
BuildText()-100%100%

File(s)

D:\a\1\s\src\ReactiveUI.Validation\Contexts\ValidationContext.cs

#LineLine coverage
 1// Copyright (c) 2020 .NET Foundation and Contributors. All rights reserved.
 2// Licensed to the .NET Foundation under one or more agreements.
 3// The .NET Foundation licenses this file to you under the MIT license.
 4// See the LICENSE file in the project root for full license information.
 5
 6using System;
 7using System.Collections.Generic;
 8using System.Collections.ObjectModel;
 9using System.Diagnostics.CodeAnalysis;
 10using System.Linq;
 11using System.Reactive;
 12using System.Reactive.Concurrency;
 13using System.Reactive.Disposables;
 14using System.Reactive.Linq;
 15using System.Reactive.Subjects;
 16using DynamicData;
 17using DynamicData.Binding;
 18using ReactiveUI.Validation.Collections;
 19using ReactiveUI.Validation.Components.Abstractions;
 20using ReactiveUI.Validation.States;
 21
 22namespace ReactiveUI.Validation.Contexts
 23{
 24    /// <inheritdoc cref="ReactiveObject" />
 25    /// <inheritdoc cref="IDisposable" />
 26    /// <inheritdoc cref="IValidationComponent" />
 27    /// <summary>
 28    /// The overall context for a view model under which validation takes place.
 29    /// </summary>
 30    /// <remarks>
 31    /// Contains all of the <see cref="ReactiveUI.Validation.Components.Abstractions.IValidationComponent" /> instances
 32    /// applicable to the view model.
 33    /// </remarks>
 34    [SuppressMessage("Usage", "CA2213:Disposable fields should be disposed", Justification = "Field _disposables dispose
 35    public class ValidationContext : ReactiveObject, IDisposable, IValidationComponent
 36    {
 6637        private readonly SourceList<IValidationComponent> _validationSource = new SourceList<IValidationComponent>();
 6638        private readonly ReplaySubject<ValidationState> _validationStatusChange = new ReplaySubject<ValidationState>(1);
 6639        private readonly ReplaySubject<bool> _validSubject = new ReplaySubject<bool>(1);
 40
 41        private readonly ReadOnlyObservableCollection<IValidationComponent> _validations;
 42        private readonly IConnectableObservable<bool> _validationConnectable;
 43        private readonly ObservableAsPropertyHelper<ValidationText> _validationText;
 44        private readonly ObservableAsPropertyHelper<bool> _isValid;
 45
 6646        private readonly CompositeDisposable _disposables = new CompositeDisposable();
 47        private bool _isActive;
 48
 49        /// <summary>
 50        /// Initializes a new instance of the <see cref="ValidationContext"/> class.
 51        /// </summary>
 52        /// <param name="scheduler">Optional scheduler to use for the properties. Uses the current thread scheduler by d
 6653        public ValidationContext(IScheduler? scheduler = null)
 54        {
 6655            scheduler ??= CurrentThreadScheduler.Instance;
 6656            _validationSource
 6657                .Connect()
 6658                .ObserveOn(scheduler)
 6659                .Bind(out _validations)
 6660                .Subscribe()
 6661                .DisposeWith(_disposables);
 62
 6663            _validationConnectable = _validations
 6664                .ToObservableChangeSet()
 6665                .ToCollection()
 6666                .StartWithEmpty()
 6667                .Select(validations =>
 13268                    validations
 17469                        .Select(v => v.ValidationStatusChange)
 13270                        .Merge())
 6671                .Switch()
 14072                .Select(_ => GetIsValid())
 6673                .Multicast(_validSubject);
 74
 6675            _isValid = _validSubject
 6676                .StartWith(true)
 6677                .ToProperty(this, m => m.IsValid, scheduler: scheduler)
 6678                .DisposeWith(_disposables);
 79
 6680            _validationText = _validSubject
 6681                .StartWith(true)
 20682                .Select(_ => BuildText())
 6683                .ToProperty(this, m => m.Text, new ValidationText(), scheduler: scheduler)
 6684                .DisposeWith(_disposables);
 85
 6686            _validSubject
 14087                .Select(_ => new ValidationState(IsValid, BuildText(), this))
 6688                .Do(_validationStatusChange.OnNext)
 6689                .Subscribe()
 6690                .DisposeWith(_disposables);
 6691        }
 92
 93        /// <summary>
 94        /// Gets an observable for the Valid state.
 95        /// </summary>
 96        public IObservable<bool> Valid
 97        {
 98            get
 99            {
 2100                Activate();
 2101                return _validSubject.AsObservable();
 102            }
 103        }
 104
 105        /// <summary>
 106        /// Gets get the list of validations.
 107        /// </summary>
 80108        public ReadOnlyObservableCollection<IValidationComponent> Validations => _validations;
 109
 110        /// <inheritdoc/>
 111        [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "Reviewed."
 112        public bool IsValid
 113        {
 114            get
 115            {
 122116                Activate();
 122117                return _isValid.Value;
 118            }
 119        }
 120
 121        /// <inheritdoc />
 122        public IObservable<ValidationState> ValidationStatusChange
 123        {
 124            get
 125            {
 10126                Activate();
 10127                return _validationStatusChange.AsObservable();
 128            }
 129        }
 130
 131        /// <inheritdoc />
 132        public ValidationText Text
 133        {
 134            get
 135            {
 18136                Activate();
 18137                return _validationText.Value;
 138            }
 139        }
 140
 141        /// <summary>
 142        /// Adds a validation into the validations collection.
 143        /// </summary>
 144        /// <param name="validation">Validation component to be added into the collection.</param>
 54145        public void Add(IValidationComponent validation) => _validationSource.Add(validation);
 146
 147        /// <summary>
 148        /// Returns if the whole context is valid checking all the validations.
 149        /// </summary>
 150        /// <returns>Returns true if the <see cref="ValidationContext"/> is valid, otherwise false.</returns>
 244151        public bool GetIsValid() => _validations.Count == 0 || _validations.All(v => v.IsValid);
 152
 153        /// <inheritdoc/>
 154        public void Dispose()
 155        {
 156            // Dispose of unmanaged resources.
 0157            Dispose(true);
 158
 159            // Suppress finalization.
 0160            GC.SuppressFinalize(this);
 0161        }
 162
 163        /// <summary>
 164        /// Disposes of the managed resources.
 165        /// </summary>
 166        /// <param name="disposing">If its getting called by the <see cref="Dispose()"/> method.</param>
 167        protected virtual void Dispose(bool disposing)
 168        {
 0169            if (disposing)
 170            {
 0171                _disposables?.Dispose();
 172            }
 0173        }
 174
 175        private void Activate()
 176        {
 152177            if (_isActive)
 178            {
 118179                return;
 180            }
 181
 34182            _isActive = true;
 34183            _disposables.Add(_validationConnectable.Connect());
 34184        }
 185
 186        /// <summary>
 187        /// Build a list of the validation text for each invalid component.
 188        /// </summary>
 189        /// <returns>
 190        /// Returns the <see cref="ValidationText"/> with all the error messages from the non valid components.
 191        /// </returns>
 192        private ValidationText BuildText() =>
 214193            new ValidationText(_validations
 418194                .Where(p => !p.IsValid && p.Text != null)
 334195                .Select(p => p.Text!));
 196    }
 197}