File: CompatibilityTable.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.Frameworks\NuGet.Frameworks.csproj (NuGet.Frameworks)
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;

namespace NuGet.Frameworks
{
    /// <summary>
    /// Creates a table of compatible frameworks.
    /// </summary>
    public class CompatibilityTable
    {
        private readonly IFrameworkNameProvider _mappings;
        private readonly IFrameworkCompatibilityProvider _compat;
        private readonly Dictionary<NuGetFramework, HashSet<NuGetFramework>> _table;
        private readonly FrameworkReducer _reducer;

        public CompatibilityTable(IEnumerable<NuGetFramework> frameworks)
            : this(frameworks,
                DefaultFrameworkNameProvider.Instance,
                DefaultCompatibilityProvider.Instance)
        {
        }

        public CompatibilityTable(IEnumerable<NuGetFramework> frameworks, IFrameworkNameProvider mappings, IFrameworkCompatibilityProvider compat)
        {
            if (frameworks is null) throw new ArgumentNullException(nameof(frameworks));
            if (mappings is null) throw new ArgumentNullException(nameof(mappings));
            if (compat is null) throw new ArgumentNullException(nameof(compat));

            _compat = compat;
            _mappings = mappings;
            _table = GetTable(frameworks, _compat);
            _reducer = new FrameworkReducer(_mappings, _compat);
        }

        /// <summary>
        /// True if the framework is in the table.
        /// </summary>
        public bool HasFramework(NuGetFramework framework)
        {
            return _table.ContainsKey(framework);
        }

        /// <summary>
        /// Gives the smallest set of frameworks from the table that cover everything the given framework would cover.
        /// </summary>
        public IEnumerable<NuGetFramework> GetNearest(NuGetFramework framework)
        {
            if (framework is null) throw new ArgumentNullException(nameof(framework));

            // start with everything compatible with the framework
            var allCompatible = _table.Keys.Where(f => _compat.IsCompatible(framework, f));

            return _reducer.ReduceUpwards(allCompatible);
        }

        /// <summary>
        /// Returns the list of all frameworks compatible with the given framework
        /// </summary>
        public bool TryGetCompatible(NuGetFramework framework, [NotNullWhen(true)] out IEnumerable<NuGetFramework>? compatible)
        {
            if (_table.TryGetValue(framework, out HashSet<NuGetFramework>? frameworks))
            {
                compatible = new HashSet<NuGetFramework>(frameworks);
                return true;
            }

            compatible = null;
            return false;
        }

        private static Dictionary<NuGetFramework, HashSet<NuGetFramework>> GetTable(IEnumerable<NuGetFramework> frameworks, IFrameworkCompatibilityProvider compat)
        {
            // get the distinct set of frameworks, ignoring all special frameworks like Any, and Unsupported
            var input = new HashSet<NuGetFramework>(frameworks.Where(f => f.IsSpecificFramework));
            var table = new Dictionary<NuGetFramework, HashSet<NuGetFramework>>();

            foreach (var framework in input)
            {
                var compatFrameworks = new HashSet<NuGetFramework>();
                table.Add(framework, compatFrameworks);

                foreach (var testFramework in input)
                {
                    if (compat.IsCompatible(framework, testFramework))
                    {
                        compatFrameworks.Add(testFramework);
                    }
                }
            }

            return table;
        }
    }
}