/// <summary> /// Create a file finder with <paramref name="directoryPattern"/>. /// </summary> /// <param name="fileSystem">Wrapper used to access file system</param> /// <param name="directoryPattern"> /// Directory path pattern. See <see cref="FileFinder"/>. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="fileSystem"/> or <paramref name="directoryPattern"/> is null /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="directoryPattern"/> contains invalid characters /// </exception> public FileFinder(IFileSystem fileSystem, string directoryPattern) { _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); Pattern = new PathPattern(directoryPattern); }
/// <summary> /// Find all folders which match <see cref="Pattern"/>. /// </summary> /// <param name="pathComparer">Search order</param> /// <param name="progress">Reports all searched directory paths</param> /// <returns>All folders which match <see cref="Pattern"/></returns> private IEnumerable <string> FindAll( IComparer <string> pathComparer, IProgress <string> progress) { var comparer = new StateComparer(pathComparer); var parts = Pattern.GetParts().ToList(); if (parts.Count == 0) { yield break; } // the root directory does not exist var firstPath = parts[0]; if (!_fileSystem.DirectoryExists(firstPath)) { yield break; } // pattern contains only the root directory if (parts.Count == 1) { yield return(firstPath); yield break; } var states = new BinaryHeap <State>(comparer); states.Enqueue(new State(firstPath, 1)); // find all directories which match given pattern while (states.Count > 0) { var state = states.Dequeue(); progress.Report(state.Path); if (state.MatchedPartCount >= parts.Count) { // this path has matched the whole pattern => return it yield return(state.Path); } else { var part = parts[state.MatchedPartCount]; if (part == PathPattern.RecursivePattern) { // This part is after the first ** pattern. // Use a different algorithm: search all directories once and use pattern // matching to determine whether to include given directory in the result. if (Pattern.Match(state.Path)) { yield return(state.Path); } // search all subdirectories exactly once foreach (var child in EnumerateDirectories(state.Path, null)) { // MatchedPartCount can be any value >= firstGeneralPatternIndex states.Enqueue(new State(child, state.MatchedPartCount)); } } else if (!PathPattern.ContainsSpecialCharacters(part)) { // path part is a relative path without any special characters var path = Path.Combine(state.Path, part); if (!_fileSystem.DirectoryExists(path)) { continue; } states.Add(new State(path, state.MatchedPartCount + 1)); } else // part has to be a simple pattern which does not contain ** { Trace.Assert(part != PathPattern.RecursivePattern); foreach (var dir in EnumerateDirectories(state.Path, part)) { states.Add(new State(dir, state.MatchedPartCount + 1)); } } } } }
/// <summary> /// Find all folders which match <see cref="Pattern"/>. /// </summary> /// <param name="pathComparer">Search order</param> /// <param name="progress">Reports all searched directory paths</param> /// <returns>Folders which match <see cref="Pattern"/></returns> private IEnumerable <string> FindAll( IComparer <string> pathComparer, IProgress <string> progress) { var comparer = new StateComparer(pathComparer); var parts = Pattern.GetParts().ToList(); if (parts.Count == 0) { yield break; } var firstPath = parts[0]; if (!_fileSystem.DirectoryExists(firstPath)) { yield break; } if (parts.Count == 1) { yield return(firstPath); yield break; } var visited = new HashSet <string>(StringComparer.CurrentCultureIgnoreCase); var states = new BinaryHeap <State>(comparer); states.Enqueue(new State(firstPath, 1)); while (states.Count > 0) { var state = states.Dequeue(); progress.Report(state.Path); if (state.MatchedPartCount >= parts.Count) { if (!visited.Contains(state.Path)) { yield return(state.Path); visited.Add(state.Path); } } else { var part = parts[state.MatchedPartCount]; if (!PathPattern.ContainsSpecialCharacters(part)) { // path part is a relative path without any special characters var path = Path.Combine(state.Path, part); if (!_fileSystem.DirectoryExists(path)) { continue; } states.Add(new State(path, state.MatchedPartCount + 1)); } else if (part == "**") { // assume the pattern has been matched states.Add(new State(state.Path, state.MatchedPartCount + 1)); // assume it has not been matched yet foreach (var dir in EnumerateDirectories(state.Path, null)) { states.Add(new State(dir, state.MatchedPartCount)); } } else { foreach (var dir in EnumerateDirectories(state.Path, part)) { states.Add(new State(dir, state.MatchedPartCount + 1)); } } } } }