Skip to content

Instantly share code, notes, and snippets.

@kumpera
Created June 19, 2018 18:58
Show Gist options
  • Select an option

  • Save kumpera/50e14362fadda5efb004d056f854b4aa to your computer and use it in GitHub Desktop.

Select an option

Save kumpera/50e14362fadda5efb004d056f854b4aa to your computer and use it in GitHub Desktop.
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