| File: Matcher.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 Microsoft.Extensions.FileSystemGlobbing.Abstractions; using Microsoft.Extensions.FileSystemGlobbing.Internal; using Microsoft.Extensions.FileSystemGlobbing.Internal.Patterns; namespace Microsoft.Extensions.FileSystemGlobbing { /// <summary> /// Searches the file system for files with names that match specified patterns. /// </summary> /// <remarks> /// <para> /// Patterns specified in <seealso cref="AddInclude(string)" /> and <seealso cref="AddExclude(string)" /> can use /// the following formats to match multiple files or directories. /// </para> /// <list type="bullet"> /// <item> /// <term> /// exact directory and file name /// </term> /// <description> /// <list type="bullet"> /// <item> /// <term>"one.txt"</term> /// </item> /// <item> /// <term>"dir/two.txt"</term> /// </item> /// </list> /// </description> /// </item> /// <item> /// <term> /// wildcards (*) in file and directory names that represent zero to many characters not including /// directory separators characters /// </term> /// <description> /// <list type="bullet"> /// <item> /// <term>"*.txt"</term><description>all files with .txt file extension</description> /// </item> /// <item> /// <term>"*.*"</term><description>all files with an extension</description> /// </item> /// <item> /// <term>"*"</term><description>all files in top level directory</description> /// </item> /// <item> /// <term>".*"</term><description>filenames beginning with '.'</description> /// </item> /// - "*word* - all files with 'word' in the filename /// <item> /// <term>"readme.*"</term> /// <description>all files named 'readme' with any file extension</description> /// </item> /// <item> /// <term>"styles/*.css"</term> /// <description>all files with extension '.css' in the directory 'styles/'</description> /// </item> /// <item> /// <term>"scripts/*/*"</term> /// <description>all files in 'scripts/' or one level of subdirectory under 'scripts/'</description> /// </item> /// <item> /// <term>"images*/*"</term> /// <description>all files in a folder with name that is or begins with 'images'</description> /// </item> /// </list> /// </description> /// </item> /// <item> /// <term>arbitrary directory depth ("/**/")</term> /// <description> /// <list type="bullet"> /// <item> /// <term>"**/*"</term><description>all files in any subdirectory</description> /// </item> /// <item> /// <term>"dir/**/*"</term><description>all files in any subdirectory under 'dir/'</description> /// </item> /// </list> /// </description> /// </item> /// <item> /// <term>relative paths</term> /// <description> /// '../shared/*' - all files in a diretory named 'shared' at the sibling level to the base directory given /// to <see cref="Execute(DirectoryInfoBase)" /> /// </description> /// </item> /// </list> /// </remarks> public class Matcher { private readonly List<IPattern>? _includePatterns; private readonly List<IPattern>? _excludePatterns; private readonly List<IncludeOrExcludeValue<IPattern>>? _includeOrExcludePatterns; private readonly PatternBuilder _builder; private readonly bool _preserveFilterOrder; internal StringComparison ComparisonType { get; } /// <summary> /// Initializes a new instance of the <see cref="Matcher" /> class using case-insensitive matching. /// </summary> public Matcher() : this(StringComparison.OrdinalIgnoreCase, false) { } /// <summary> /// Initializes a new instance of the <see cref="Matcher" /> class using the string comparison method specified. /// </summary> /// <param name="comparisonType">The <see cref="StringComparison" /> to use</param> public Matcher(StringComparison comparisonType) : this(comparisonType, false) { } /// <summary> /// Initializes a new instance of the <see cref="Matcher" /> class using the string comparison method and filter ordering specified. /// </summary> /// <param name="comparisonType">The <see cref="StringComparison" /> to use</param> /// <param name="preserveFilterOrder"> /// <see langword="true" /> if the filters should be applied in the order they were added; <see langword="false" /> /// if the inclusion filters should be applied before the exclusion filters /// </param> public Matcher(StringComparison comparisonType = StringComparison.OrdinalIgnoreCase, bool preserveFilterOrder = false) { ComparisonType = comparisonType; _builder = new PatternBuilder(comparisonType); _preserveFilterOrder = preserveFilterOrder; if (preserveFilterOrder) { _includeOrExcludePatterns = []; } else { _includePatterns = []; _excludePatterns = []; } } /// <summary> /// <para> /// Add a file name pattern that the matcher should use to discover files. Patterns are relative to the root /// directory given when <see cref="Execute(DirectoryInfoBase)" /> is called. /// </para> /// <para> /// Use the forward slash '/' to represent directory separator. Use '*' to represent wildcards in file and /// directory names. Use '**' to represent arbitrary directory depth. Use '..' to represent a parent directory. /// </para> /// </summary> /// <param name="pattern">The globbing pattern</param> /// <returns>The matcher</returns> public virtual Matcher AddInclude(string pattern) { if (_preserveFilterOrder) _includeOrExcludePatterns!.Add(new IncludeOrExcludeValue<IPattern> { Value = _builder.Build(pattern), IsInclude = true }); else _includePatterns!.Add(_builder.Build(pattern)); return this; } /// <summary> /// <para> /// Add a file name pattern for files the matcher should exclude from the results. Patterns are relative to the /// root directory given when <see cref="Execute(DirectoryInfoBase)" /> is called. /// </para> /// <para> /// Use the forward slash '/' to represent directory separator. Use '*' to represent wildcards in file and /// directory names. Use '**' to represent arbitrary directory depth. Use '..' to represent a parent directory. /// </para> /// </summary> /// <param name="pattern">The globbing pattern</param> /// <returns>The matcher</returns> public virtual Matcher AddExclude(string pattern) { if (_preserveFilterOrder) _includeOrExcludePatterns!.Add(new IncludeOrExcludeValue<IPattern> { Value = _builder.Build(pattern), IsInclude = false }); else _excludePatterns!.Add(_builder.Build(pattern)); return this; } /// <summary> /// Searches the directory specified for all files matching patterns added to this instance of <see cref="Matcher" /> /// </summary> /// <param name="directoryInfo">The root directory for the search</param> /// <returns>Always returns instance of <see cref="PatternMatchingResult" />, even if no files were matched</returns> public virtual PatternMatchingResult Execute(DirectoryInfoBase directoryInfo) { ArgumentNullException.ThrowIfNull(directoryInfo); return _preserveFilterOrder ? new MatcherContext(_includeOrExcludePatterns!, directoryInfo, ComparisonType).Execute() : new MatcherContext(_includePatterns!, _excludePatterns!, directoryInfo, ComparisonType).Execute(); } } }