| File: Internal\MatcherContext.cs | Web Access |
| Project: src\runtime\src\libraries\Microsoft.Extensions.FileSystemGlobbing\src\Microsoft.Extensions.FileSystemGlobbing.csproj (Microsoft.Extensions.FileSystemGlobbing) |
// 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.Extensions.FileSystemGlobbing.Abstractions; using Microsoft.Extensions.FileSystemGlobbing.Internal.PathSegments; using Microsoft.Extensions.FileSystemGlobbing.Internal.PatternContexts; using Microsoft.Extensions.FileSystemGlobbing.Util; namespace Microsoft.Extensions.FileSystemGlobbing.Internal { /// <summary> /// This API supports infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary> public class MatcherContext { private readonly DirectoryInfoBase _root; private readonly IPatternContext _patternContext; private readonly List<FilePatternMatch> _files; private readonly HashSet<string> _declaredLiteralFolderSegmentInString; private readonly HashSet<LiteralPathSegment> _declaredLiteralFileSegments = new HashSet<LiteralPathSegment>(); private bool _declaredParentPathSegment; private bool _declaredWildcardPathSegment; public MatcherContext(IEnumerable<IPattern> includePatterns, IEnumerable<IPattern> excludePatterns, DirectoryInfoBase directoryInfo, StringComparison comparison) { _root = directoryInfo; _files = []; _declaredLiteralFolderSegmentInString = new HashSet<string>(StringComparisonHelper.GetStringComparer(comparison)); IPatternContext[] includePatternContexts = includePatterns.Select(pattern => pattern.CreatePatternContextForInclude()).ToArray(); IPatternContext[] excludePatternContexts = excludePatterns.Select(pattern => pattern.CreatePatternContextForExclude()).ToArray(); _patternContext = new IncludesFirstCompositePatternContext(includePatternContexts, excludePatternContexts); } internal MatcherContext(List<IncludeOrExcludeValue<IPattern>> orderedPatterns, DirectoryInfoBase directoryInfo, StringComparison comparison) { _root = directoryInfo; _files = []; _declaredLiteralFolderSegmentInString = new HashSet<string>(StringComparisonHelper.GetStringComparer(comparison)); IncludeOrExcludeValue<IPatternContext>[] includeOrExcludePatternContexts = orderedPatterns .Select(item => new IncludeOrExcludeValue<IPatternContext> { Value = item.IsInclude ? item.Value.CreatePatternContextForInclude() : item.Value.CreatePatternContextForExclude(), IsInclude = item.IsInclude }) .ToArray(); _patternContext = new PreserveOrderCompositePatternContext(includeOrExcludePatternContexts); } public PatternMatchingResult Execute() { _files.Clear(); Match(_root, parentRelativePath: null); return new PatternMatchingResult(_files, _files.Count > 0); } private void Match(DirectoryInfoBase directory, string? parentRelativePath) { // Request all the including and excluding patterns to push current directory onto their status stack. _patternContext.PushDirectory(directory); Declare(); var entities = new List<FileSystemInfoBase?>(); if (_declaredWildcardPathSegment || _declaredLiteralFileSegments.Count != 0) { entities.AddRange(directory.EnumerateFileSystemInfos()); } else { IEnumerable<FileSystemInfoBase> candidates = directory.EnumerateFileSystemInfos(); foreach (FileSystemInfoBase candidate in candidates) { if (candidate is DirectoryInfoBase && _declaredLiteralFolderSegmentInString.Contains(candidate.Name)) { entities.Add(candidate); } } } if (_declaredParentPathSegment) { entities.Add(directory.GetDirectory("..")); } // collect files and sub directories var subDirectories = new List<DirectoryInfoBase>(); foreach (FileSystemInfoBase? entity in entities) { if (entity is FileInfoBase fileInfo) { PatternTestResult result = _patternContext.Test(fileInfo); if (result.IsSuccessful) { _files.Add(new FilePatternMatch( path: CombinePath(parentRelativePath, fileInfo.Name), stem: result.Stem)); } continue; } if (entity is DirectoryInfoBase directoryInfo) { if (_patternContext.Test(directoryInfo)) { subDirectories.Add(directoryInfo); } continue; } } // Matches the sub directories recursively foreach (DirectoryInfoBase subDir in subDirectories) { string relativePath = CombinePath(parentRelativePath, subDir.Name); Match(subDir, relativePath); } // Request all the including and excluding patterns to pop their status stack. _patternContext.PopDirectory(); } private void Declare() { _declaredLiteralFileSegments.Clear(); _declaredParentPathSegment = false; _declaredWildcardPathSegment = false; _patternContext.Declare(DeclareInclude); } private void DeclareInclude(IPathSegment patternSegment, bool isLastSegment) { if (patternSegment is LiteralPathSegment literalSegment) { if (isLastSegment) { _declaredLiteralFileSegments.Add(literalSegment); } else { _declaredLiteralFolderSegmentInString.Add(literalSegment.Value); } } else if (patternSegment is ParentPathSegment) { _declaredParentPathSegment = true; } else if (patternSegment is WildcardPathSegment) { _declaredWildcardPathSegment = true; } } internal static string CombinePath(string? left, string right) { if (string.IsNullOrEmpty(left)) { return right; } else { return $"{left}/{right}"; } } } }