File: BackEnd\BuildManager\RequestedProjectState.cs
Web Access
Project: ..\..\..\src\Build\Microsoft.Build.csproj (Microsoft.Build)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Build.BackEnd;
 
#nullable disable
 
namespace Microsoft.Build.Execution
{
    /// <summary>
    /// Interface defining properties, items, and metadata of interest for a <see cref="BuildRequestData"/>.
    /// </summary>
    public class RequestedProjectState : ITranslatable
    {
        private List<string> _propertyFilters;
        private IDictionary<string, List<string>> _itemFilters;
 
        /// <summary>
        /// Properties of interest.
        /// </summary>
        public List<string> PropertyFilters
        {
            get => _propertyFilters;
            set => _propertyFilters = value;
        }
 
        /// <summary>
        /// Items and metadata of interest.
        /// </summary>
        public IDictionary<string, List<string>> ItemFilters
        {
            get => _itemFilters;
            set => _itemFilters = value;
        }
 
        /// <summary>
        /// Creates a deep copy of this instance.
        /// </summary>
        internal RequestedProjectState DeepClone()
        {
            RequestedProjectState result = new RequestedProjectState();
            if (PropertyFilters is not null)
            {
                result.PropertyFilters = new List<string>(PropertyFilters);
            }
            if (ItemFilters is not null)
            {
                result.ItemFilters = ItemFilters.ToDictionary(
                    kvp => kvp.Key,
                    kvp => kvp.Value == null ? null : new List<string>(kvp.Value));
            }
            return result;
        }
 
        /// <summary>
        /// Returns true if this instance contains all property and item filters present in another instance.
        /// </summary>
        /// <param name="another">The instance to compare against.</param>
        /// <returns>True if this instance is equivalent or a strict subset of <paramref name="another"/>.</returns>
        internal bool IsSubsetOf(RequestedProjectState another)
        {
            if (PropertyFilters is null)
            {
                if (another.PropertyFilters is not null)
                {
                    // The instance to compare against has filtered props and we need everything -> not a subset.
                    return false;
                }
            }
            else if (another.PropertyFilters is not null)
            {
                HashSet<string> anotherPropertyFilters = new HashSet<string>(another.PropertyFilters);
                foreach (string propertyFilter in PropertyFilters)
                {
                    if (!anotherPropertyFilters.Contains(propertyFilter))
                    {
                        return false;
                    }
                }
            }
 
            if (ItemFilters is null)
            {
                if (another.ItemFilters is not null)
                {
                    // The instance to compare against has filtered items and we need everything -> not a subset.
                    return false;
                }
            }
            else if (another.ItemFilters is not null)
            {
                foreach (KeyValuePair<string, List<string>> kvp in ItemFilters)
                {
                    if (!another.ItemFilters.TryGetValue(kvp.Key, out List<string> metadata))
                    {
                        // The instance to compare against doesn't have this item -> not a subset.
                        return false;
                    }
                    if (kvp.Value is null)
                    {
                        if (metadata is not null)
                        {
                            // The instance to compare against has filtered metadata for this item and we need everything - not a subset.
                            return false;
                        }
                    }
                    else if (metadata is not null)
                    {
                        HashSet<string> anotherMetadata = new HashSet<string>(metadata);
                        foreach (string metadatum in kvp.Value)
                        {
                            if (!anotherMetadata.Contains(metadatum))
                            {
                                return false;
                            }
                        }
                    }
                }
            }
 
            return true;
        }
 
        void ITranslatable.Translate(ITranslator translator)
        {
            translator.Translate(ref _propertyFilters);
            translator.TranslateDictionary(ref _itemFilters, TranslateString, TranslateMetadataForItem, CreateItemMetadataDictionary);
        }
 
        private static IDictionary<string, List<string>> CreateItemMetadataDictionary(int capacity)
        {
            return new Dictionary<string, List<string>>(capacity, StringComparer.OrdinalIgnoreCase);
        }
 
        private static void TranslateMetadataForItem(ITranslator translator, ref List<string> list)
        {
            translator.Translate(ref list);
        }
 
        private static void TranslateString(ITranslator translator, ref string s)
        {
            translator.Translate(ref s);
        }
    }
}