File: Filtering\DocIdSymbolFilter.cs
Web Access
Project: src\sdk\src\Compatibility\Microsoft.DotNet.ApiSymbolExtensions\Microsoft.DotNet.ApiSymbolExtensions.csproj (Microsoft.DotNet.ApiSymbolExtensions)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.CodeAnalysis;

namespace Microsoft.DotNet.ApiSymbolExtensions.Filtering
{
    /// <summary>
    /// Implements the logic of filtering api.
    /// Reads the file with the list of attributes, types, members in DocId format.
    /// </summary>
    public class DocIdSymbolFilter : ISymbolFilter
    {
        private readonly HashSet<string> _docIds;
        private readonly bool _includeDocIds;

        /// <summary>
        /// Creates a filter based on the DocIDs provided in the specified files.
        /// </summary>
        /// <param name="filesWithDocIds">A collection of files each containing multiple DocIDs.</param>
        /// <param name="includeDocIds">When <see langword="false"/> (the default), symbols matching the DocIDs are
        /// excluded. When <see langword="true"/>, only symbols matching the DocIDs are included.</param>
        /// <returns>An instance of the symbol filter.</returns>
        public static DocIdSymbolFilter CreateFromFiles(string[] filesWithDocIds, bool includeDocIds = false)
        {
            List<string> docIds = new();

            foreach (string docIdsFile in filesWithDocIds)
            {
                if (string.IsNullOrWhiteSpace(docIdsFile))
                {
                    continue;
                }

                foreach (string docId in ReadDocIdsFromList(File.ReadAllLines(docIdsFile)))
                {
                    docIds.Add(docId);
                }
            }

            return new DocIdSymbolFilter(docIds, includeDocIds);
        }

        /// <summary>
        /// Creates a filter based on the DocIDs provided in the specified list.
        /// </summary>
        /// <param name="docIds">A collection of DocIDs.</param>
        /// <param name="includeDocIds">When <see langword="false"/> (the default), symbols matching the DocIDs are
        /// excluded. When <see langword="true"/>, only symbols matching the DocIDs are included.</param>
        /// <returns>An instance of the symbol filter.</returns>
        public static DocIdSymbolFilter CreateFromLists(string[] docIds, bool includeDocIds = false)
            => new DocIdSymbolFilter(ReadDocIdsFromList(docIds), includeDocIds);

        // Private constructor to avoid creating an instance with an empty list.
        private DocIdSymbolFilter(IEnumerable<string> docIds, bool includeDocIds)
        {
            _docIds = [.. docIds];
            _includeDocIds = includeDocIds;
        }

        /// <summary>
        ///  Determines whether the <see cref="ISymbol"/> should be included.
        /// </summary>
        /// <param name="symbol"><see cref="ISymbol"/> to evaluate.</param>
        /// <returns>True to include the <paramref name="symbol"/> or false to filter it out.</returns>
        public bool Include(ISymbol symbol)
        {
            string? docId = symbol.GetDocumentationCommentId();
            if (docId is not null && _docIds.Contains(docId))
            {
                return _includeDocIds;
            }

            return !_includeDocIds;
        }

        private static IEnumerable<string> ReadDocIdsFromList(params string[] ids)
        {
            foreach (string id in ids)
            {
                if (!string.IsNullOrWhiteSpace(id) && !id.StartsWith('#') && !id.StartsWith("//"))
                {
                    yield return id.Trim();
                }
            }
        }
    }
}