Ejemplo n.º 1
0
        /// <inheritdoc />
        protected override IEnumerable <UPath> EnumeratePathsImpl(UPath path, string searchPattern, SearchOption searchOption, SearchTarget searchTarget)
        {
            // Use the search pattern to normalize the path/search pattern
            var search = SearchPattern.Parse(ref path, ref searchPattern);

            // Query all mounts just once
            List <KeyValuePair <UPath, IFileSystem> > mounts;

            lock (_mounts)
            {
                mounts = _mounts.ToList();
            }

            // Internal method used to retrieve the list of search locations
            List <SearchLocation> GetSearchLocations(UPath basePath)
            {
                var locations    = new List <SearchLocation>();
                var matchedMount = false;

                foreach (var kvp in mounts)
                {
                    // Check if path partially matches a mount name
                    var remainingPath = GetRemaining(basePath, kvp.Key);
                    if (!remainingPath.IsNull && remainingPath != UPath.Root)
                    {
                        locations.Add(new SearchLocation(this, basePath, remainingPath));
                        continue;
                    }

                    if (!matchedMount)
                    {
                        // Check if path fully matches a mount name
                        remainingPath = GetRemaining(kvp.Key, basePath);
                        if (!remainingPath.IsNull)
                        {
                            matchedMount = true; // don't check other mounts, we don't want to merge them together

                            if (kvp.Value.DirectoryExists(remainingPath))
                            {
                                locations.Add(new SearchLocation(kvp.Value, kvp.Key, remainingPath));
                            }
                        }
                    }
                }

                if (!matchedMount && NextFileSystem != null && NextFileSystem.DirectoryExists(basePath))
                {
                    locations.Add(new SearchLocation(NextFileSystem, null, basePath));
                }

                return(locations);
            }

            var directoryToVisit = new List <UPath>();

            directoryToVisit.Add(path);

            var entries           = new SortedSet <UPath>(UPath.DefaultComparerIgnoreCase);
            var sortedDirectories = new SortedSet <UPath>(UPath.DefaultComparerIgnoreCase);

            var first = true;

            while (directoryToVisit.Count > 0)
            {
                var pathToVisit = directoryToVisit[0];
                directoryToVisit.RemoveAt(0);
                var dirIndex = 0;
                entries.Clear();
                sortedDirectories.Clear();

                var locations = GetSearchLocations(pathToVisit);

                // Only need to search within one filesystem, no need to sort or do other work
                if (locations.Count == 1 && locations[0].FileSystem != this && (!first || searchOption == SearchOption.AllDirectories))
                {
                    var last = locations[0];
                    foreach (var item in last.FileSystem.EnumeratePaths(last.Path, searchPattern, searchOption, searchTarget))
                    {
                        yield return(CombinePrefix(last.Prefix, item));
                    }
                }
                else
                {
                    for (var i = locations.Count - 1; i >= 0; i--)
                    {
                        var location   = locations[i];
                        var fileSystem = location.FileSystem;
                        var searchPath = location.Path;

                        if (fileSystem == this)
                        {
                            // List a single part of a mount name, queue it to be visited if needed
                            var mountPart = new UPath(searchPath.GetFirstDirectory(out _)).ToRelative();
                            var mountPath = location.Prefix / mountPart;

                            var isMatching = search.Match(mountPath);
                            if (isMatching && searchTarget != SearchTarget.File)
                            {
                                entries.Add(mountPath);
                            }

                            if (searchOption == SearchOption.AllDirectories)
                            {
                                sortedDirectories.Add(mountPath);
                            }
                        }
                        else
                        {
                            // List files in the mounted filesystems, merged and sorted into one list
                            foreach (var item in fileSystem.EnumeratePaths(searchPath, "*", SearchOption.TopDirectoryOnly, SearchTarget.Both))
                            {
                                var publicName = CombinePrefix(location.Prefix, item);
                                if (entries.Contains(publicName))
                                {
                                    continue;
                                }

                                var isFile      = fileSystem.FileExists(item);
                                var isDirectory = fileSystem.DirectoryExists(item);
                                var isMatching  = search.Match(publicName);

                                if (isMatching && ((isFile && searchTarget != SearchTarget.Directory) || (isDirectory && searchTarget != SearchTarget.File)))
                                {
                                    entries.Add(publicName);
                                }

                                if (searchOption == SearchOption.AllDirectories && isDirectory)
                                {
                                    sortedDirectories.Add(publicName);
                                }
                            }
                        }
                    }
                }

                if (first)
                {
                    if (locations.Count == 0 && path != UPath.Root)
                    {
                        throw NewDirectoryNotFoundException(path);
                    }

                    first = false;
                }

                // Enqueue directories and respect order
                foreach (var nextDir in sortedDirectories)
                {
                    directoryToVisit.Insert(dirIndex++, nextDir);
                }

                // Return entries
                foreach (var entry in entries)
                {
                    yield return(entry);
                }
            }
        }