Created
June 19, 2018 18:58
-
-
Save kumpera/50e14362fadda5efb004d056f854b4aa to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Reflection; | |
| using System.Reflection.Emit; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| // using Microsoft.Extensions.FileSystemGlobbing.Abstractions; | |
| // using Microsoft.Extensions.FileSystemGlobbing.Internal.PathSegments; | |
| // using Microsoft.Extensions.FileSystemGlobbing.Util; | |
| using Microsoft.Extensions.FileSystemGlobbing.Internal; | |
| class Program | |
| { | |
| static void Main (string[] args) { | |
| // Console.WriteLine (Test ("bla", x => x + "_")); | |
| var m = new MatcherContext ( | |
| new IPattern [1] { new MockInclude () }, | |
| new IPattern [0], | |
| new MockDirectoryInfo ("/root"), | |
| StringComparison.InvariantCulture); | |
| m.Execute (); | |
| } | |
| } | |
| namespace Microsoft.Extensions.FileSystemGlobbing.Internal | |
| { | |
| public class MockDirectoryInfo : DirectoryInfoBase { | |
| string name; | |
| public MockDirectoryInfo (string n) { | |
| name = n; | |
| } | |
| public override IEnumerable<FileSystemInfoBase> EnumerateFileSystemInfos() | |
| { | |
| return new FileSystemInfoBase[1] { | |
| new MockDirectoryInfo (name + "/bla") | |
| }; | |
| // throw new Exception (); | |
| } | |
| public override string Name { | |
| get { | |
| return name; | |
| } | |
| } | |
| public override DirectoryInfoBase GetDirectory(string path){ | |
| throw new Exception ("dh"); | |
| } | |
| } | |
| public class MockInclude : IPattern { | |
| public IPatternContext CreatePatternContextForInclude () { | |
| return new MockPatternContext (); | |
| } | |
| public IPatternContext CreatePatternContextForExclude () { | |
| throw new Exception ("llll"); | |
| } | |
| } | |
| public class MockPatternContext : IPatternContext { | |
| public bool Test(DirectoryInfoBase directory) { | |
| Console.WriteLine (">>>"); | |
| return true; | |
| } | |
| public void PushDirectory(DirectoryInfoBase directory) { | |
| Console.WriteLine ("PushDirectory"); | |
| // throw new Exception ("999"); | |
| } | |
| public void PopDirectory() { | |
| throw new Exception ("888"); | |
| } | |
| public void Declare(Action<IPathSegment, bool> onDeclare) | |
| { | |
| Console.WriteLine ("Declare"); | |
| onDeclare (new LiteralPathSegment ("root"), true); | |
| } | |
| public PatternTestResult Test(FileInfoBase file) { | |
| throw new Exception ( "dfarq"); | |
| } | |
| } | |
| public class ParentPathSegment : IPathSegment | |
| { | |
| } | |
| public class PatternMatchingResult { | |
| public PatternMatchingResult(IEnumerable<FilePatternMatch> files) | |
| : this(files, hasMatches: files.Any()) | |
| { | |
| Files = files; | |
| } | |
| public PatternMatchingResult(IEnumerable<FilePatternMatch> files, bool hasMatches) | |
| { | |
| Files = files; | |
| HasMatches = hasMatches; | |
| } | |
| public IEnumerable<FilePatternMatch> Files { get; set; } | |
| public bool HasMatches { get; } } | |
| public interface IPattern { | |
| IPatternContext CreatePatternContextForInclude (); | |
| IPatternContext CreatePatternContextForExclude (); | |
| } | |
| public abstract class FileSystemInfoBase | |
| { | |
| public abstract string Name { get; } | |
| } | |
| public abstract class DirectoryInfoBase : FileSystemInfoBase { | |
| public abstract IEnumerable<FileSystemInfoBase> EnumerateFileSystemInfos(); | |
| public abstract DirectoryInfoBase GetDirectory(string path); | |
| } | |
| public interface IPathSegment {} | |
| public class LiteralPathSegment : IPathSegment { | |
| public LiteralPathSegment(string v) { | |
| Value = v; | |
| } | |
| public string Value { get; } | |
| } | |
| public class WildcardPathSegment : IPathSegment { | |
| } | |
| public struct PatternTestResult | |
| { | |
| public static readonly PatternTestResult Failed = new PatternTestResult(isSuccessful: false, stem: null); | |
| public bool IsSuccessful { get; } | |
| public string Stem { get; } | |
| private PatternTestResult(bool isSuccessful, string stem) | |
| { | |
| IsSuccessful = isSuccessful; | |
| Stem = stem; | |
| } | |
| public static PatternTestResult Success(string stem) | |
| { | |
| return new PatternTestResult(isSuccessful: true, stem: stem); | |
| } | |
| } | |
| public struct FilePatternMatch : IEquatable<FilePatternMatch> | |
| { | |
| public string Path { get; } | |
| public string Stem { get; } | |
| public FilePatternMatch(string path, string stem) | |
| { | |
| Path = path; | |
| Stem = stem; | |
| } | |
| public bool Equals(FilePatternMatch other) | |
| { | |
| return string.Equals(other.Path, Path, StringComparison.OrdinalIgnoreCase) && | |
| string.Equals(other.Stem, Stem, StringComparison.OrdinalIgnoreCase); | |
| } | |
| public override bool Equals(object obj) | |
| { | |
| return Equals((FilePatternMatch) obj); | |
| } | |
| public override int GetHashCode() | |
| { | |
| // var hashCodeCombiner = HashCodeCombiner.Start(); | |
| // hashCodeCombiner.Add(Path, StringComparer.OrdinalIgnoreCase); | |
| // hashCodeCombiner.Add(Stem, StringComparer.OrdinalIgnoreCase); | |
| // | |
| // return hashCodeCombiner; | |
| throw new Exception ("kkkk"); | |
| // return 12334; | |
| } | |
| } | |
| public abstract class FileInfoBase : FileSystemInfoBase | |
| { | |
| } | |
| public interface IPatternContext | |
| { | |
| void Declare(Action<IPathSegment, bool> onDeclare); | |
| bool Test(DirectoryInfoBase directory); | |
| PatternTestResult Test(FileInfoBase file); | |
| void PushDirectory(DirectoryInfoBase directory); | |
| void PopDirectory(); | |
| } | |
| public class MatcherContext | |
| { | |
| private readonly DirectoryInfoBase _root; | |
| private readonly List<IPatternContext> _includePatternContexts; | |
| private readonly List<IPatternContext> _excludePatternContexts; | |
| private readonly List<FilePatternMatch> _files; | |
| // | |
| private readonly HashSet<string> _declaredLiteralFolderSegmentInString; | |
| private readonly HashSet<LiteralPathSegment> _declaredLiteralFolderSegments = new HashSet<LiteralPathSegment>(); | |
| private readonly HashSet<LiteralPathSegment> _declaredLiteralFileSegments = new HashSet<LiteralPathSegment>(); | |
| // | |
| private bool _declaredParentPathSegment; | |
| private bool _declaredWildcardPathSegment; | |
| // | |
| private readonly StringComparison _comparisonType; | |
| // | |
| public MatcherContext( | |
| IEnumerable<IPattern> includePatterns, | |
| IEnumerable<IPattern> excludePatterns, | |
| DirectoryInfoBase directoryInfo, | |
| StringComparison comparison) | |
| { | |
| _root = directoryInfo; | |
| _files = new List<FilePatternMatch>(); | |
| _comparisonType = comparison; | |
| _includePatternContexts = includePatterns.Select(pattern => pattern.CreatePatternContextForInclude()).ToList(); | |
| _excludePatternContexts = excludePatterns.Select(pattern => pattern.CreatePatternContextForExclude()).ToList(); | |
| _declaredLiteralFolderSegmentInString = new HashSet<string>(StringComparisonHelper.GetStringComparer(comparison)); | |
| } | |
| 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. | |
| PushDirectory(directory); | |
| Declare(); | |
| var entities = new List<FileSystemInfoBase>(); | |
| if (_declaredWildcardPathSegment || _declaredLiteralFileSegments.Any()) | |
| { | |
| entities.AddRange(directory.EnumerateFileSystemInfos ()); | |
| } | |
| else | |
| { | |
| var candidates = directory.EnumerateFileSystemInfos().OfType<DirectoryInfoBase>(); | |
| foreach (var candidate in candidates) | |
| { | |
| if (_declaredLiteralFolderSegmentInString.Contains(candidate.Name)) | |
| { | |
| entities.Add(candidate); | |
| } | |
| } | |
| } | |
| if (_declaredParentPathSegment) | |
| { | |
| entities.Add(directory.GetDirectory("..")); | |
| } | |
| // collect files and sub directories | |
| var subDirectories = new List<DirectoryInfoBase>(); | |
| foreach (var entity in entities) | |
| { | |
| var fileInfo = entity as FileInfoBase; | |
| if (fileInfo != null) | |
| { | |
| var result = MatchPatternContexts(fileInfo, (pattern, file) => pattern.Test(file)); | |
| if (result.IsSuccessful) | |
| { | |
| _files.Add(new FilePatternMatch( | |
| path: CombinePath(parentRelativePath, fileInfo.Name), | |
| stem: result.Stem)); | |
| } | |
| continue; | |
| } | |
| var directoryInfo = entity as DirectoryInfoBase; | |
| if (directoryInfo != null) | |
| { | |
| if (MatchPatternContexts(directoryInfo, (pattern, dir) => pattern.Test(dir))) | |
| { | |
| subDirectories.Add(directoryInfo); | |
| } | |
| continue; | |
| } | |
| } | |
| // Matches the sub directories recursively | |
| foreach (var subDir in subDirectories) | |
| { | |
| var relativePath = CombinePath(parentRelativePath, subDir.Name); | |
| Match(subDir, relativePath); | |
| } | |
| // Request all the including and excluding patterns to pop their status stack. | |
| PopDirectory(); | |
| } | |
| private void Declare() | |
| { | |
| _declaredLiteralFileSegments.Clear(); | |
| _declaredLiteralFolderSegments.Clear(); | |
| _declaredParentPathSegment = false; | |
| _declaredWildcardPathSegment = false; | |
| foreach (var include in _includePatternContexts) | |
| { | |
| include.Declare(DeclareInclude); | |
| } | |
| } | |
| private void DeclareInclude(IPathSegment patternSegment, bool isLastSegment) | |
| { | |
| var literalSegment = patternSegment as LiteralPathSegment; | |
| if (literalSegment != null) | |
| { | |
| if (isLastSegment) | |
| { | |
| _declaredLiteralFileSegments.Add(literalSegment); | |
| } | |
| else | |
| { | |
| _declaredLiteralFolderSegments.Add(literalSegment); | |
| _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 string.Format("{0}/{1}", left, right); | |
| } | |
| } | |
| // | |
| // Used to adapt Test(DirectoryInfoBase) for the below overload | |
| private bool MatchPatternContexts<TFileInfoBase>(TFileInfoBase fileinfo, Func<IPatternContext, TFileInfoBase, bool> test) | |
| { | |
| return MatchPatternContexts( | |
| fileinfo, | |
| (ctx, file) => | |
| { | |
| if (test(ctx, file)) | |
| { | |
| return PatternTestResult.Success(stem: string.Empty); | |
| } | |
| else | |
| { | |
| return PatternTestResult.Failed; | |
| } | |
| }).IsSuccessful; | |
| } | |
| private PatternTestResult MatchPatternContexts<TFileInfoBase>(TFileInfoBase fileinfo, Func<IPatternContext, TFileInfoBase, PatternTestResult> test) | |
| { | |
| var result = PatternTestResult.Failed; | |
| Console.WriteLine ("hello?"); | |
| // If the given file/directory matches any including pattern, continues to next step. | |
| foreach (var context in _includePatternContexts) | |
| { | |
| var localResult = test(context, fileinfo); | |
| if (localResult.IsSuccessful) | |
| { | |
| result = localResult; | |
| break; | |
| } | |
| } | |
| // If the given file/directory doesn't match any of the including pattern, returns false. | |
| if (!result.IsSuccessful) | |
| { | |
| return PatternTestResult.Failed; | |
| } | |
| // If the given file/directory matches any excluding pattern, returns false. | |
| foreach (var context in _excludePatternContexts) | |
| { | |
| if (test(context, fileinfo).IsSuccessful) | |
| { | |
| return PatternTestResult.Failed; | |
| } | |
| } | |
| return result; | |
| } | |
| private void PopDirectory() | |
| { | |
| foreach (var context in _excludePatternContexts) | |
| { | |
| context.PopDirectory(); | |
| } | |
| foreach (var context in _includePatternContexts) | |
| { | |
| context.PopDirectory(); | |
| } | |
| } | |
| private void PushDirectory(DirectoryInfoBase directory) | |
| { | |
| foreach (var context in _includePatternContexts) | |
| { | |
| context.PushDirectory(directory); | |
| } | |
| foreach (var context in _excludePatternContexts) | |
| { | |
| context.PushDirectory(directory); | |
| } | |
| } | |
| } | |
| internal static class StringComparisonHelper | |
| { | |
| public static StringComparer GetStringComparer(StringComparison comparisonType) | |
| { | |
| switch (comparisonType) | |
| { | |
| case StringComparison.CurrentCulture: | |
| return StringComparer.CurrentCulture; | |
| case StringComparison.CurrentCultureIgnoreCase: | |
| return StringComparer.CurrentCultureIgnoreCase; | |
| case StringComparison.Ordinal: | |
| return StringComparer.Ordinal; | |
| case StringComparison.OrdinalIgnoreCase: | |
| return StringComparer.OrdinalIgnoreCase; | |
| case StringComparison.InvariantCulture: | |
| return StringComparer.InvariantCulture; | |
| case StringComparison.InvariantCultureIgnoreCase: | |
| return StringComparer.InvariantCultureIgnoreCase; | |
| default: | |
| throw new InvalidOperationException($"Unexpected StringComparison type: {comparisonType}"); | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment