/// <summary>
        /// Express a local path as a known root and a relative canonicalised path.
        /// </summary>
        /// <remarks>
        /// Finds the storage root closest to the path, ie. smallest enclosing subtree.
        /// Returns false if no root can be found, or if the qualified path exists under a graft
        /// point and is therefore 'shadowed'.
        /// </remarks>
        public bool TryResolveQualifiedPath(string absolutePath, out QualifiedPath qualifiedPath)
        {
            qualifiedPath = default;
            var uri = new Uri(absolutePath);

            foreach (var root in rootsLongestToShortest)
            {
                if (!PathUtils.TryGetRelativeSegments(root.RootUri, uri, out var relativePathParts))
                {
                    continue;
                }
                var canonicalisedPath = fileSystemApi.GetCanonicalRelativePath(root.RootUri.LocalPath, relativePathParts);

                // Found a 'best' qualified path.
                qualifiedPath = new QualifiedPath(root, canonicalisedPath);

                if (!graftsByParent.TryGetValue(root, out var grafts))
                {
                    return(true);                                                   // No grafts present to 'shadow' this hierarchy.
                }
                var comparer = new PathEqualityComparer(root.Casing);
                if (grafts.Any(g => g.GraftPoint.RelativePath.Contains(canonicalisedPath, comparer)))
                {
                    // The qualified path is shadowed by a graft point.
                    return(false);
                }
                return(true);
            }
            // No containing root was found.
            return(false);
        }
Example #2
0
 public Graft(QualifiedPath graftPoint, LocalRoot childRoot)
 {
     if (!graftPoint.RelativePath.IsContainer())
     {
         throw new ArgumentException($"Graft point must be a container: {graftPoint}", nameof(graftPoint));
     }
     GraftPoint = graftPoint;
     ChildRoot  = childRoot ?? throw new ArgumentNullException(nameof(childRoot));
 }
        /// <summary>
        /// Resolve the qualified path to an OS path and return a FileInfo wrapper.
        /// </summary>
        public FileInfo ResolveToFile(QualifiedPath qualifiedPath)
        {
            AssertLocalRootExistsInModel(qualifiedPath.Root);
            if (qualifiedPath.RelativePath.IsContainer())
            {
                throw new ArgumentException($"Cannot resolve a container path to a file: {qualifiedPath}", nameof(qualifiedPath));
            }
            var absoluteUri = new Uri(qualifiedPath.Root.RootUri, qualifiedPath.RelativePath.ToString());

            return(new FileInfo(absoluteUri.LocalPath));
        }
        /// <summary>
        /// Find the named root which ultimately owns the path.
        /// </summary>
        public NamedRoot FindNamedRoot(QualifiedPath path)
        {
            var currentNode = path.Root;

            AssertLocalRootExistsInModel(currentNode);
            var traversed = new HashSet <LocalRoot> {
                currentNode
            };

            while (graftsByChild.TryGetValue(currentNode, out var graft))
            {
                currentNode = graft.ChildRoot;
                // Sanity check. Should be impossible to configure such a situation.
                if (!traversed.Add(currentNode))
                {
                    throw new InvalidOperationException("Cycle detected in graph.");
                }
            }
            return(namedRoots.FirstOrDefault(r => r.LocalRoot == currentNode));
        }
        public AbstractPath MapToAbstractPath(QualifiedPath qualifiedPath, IDiagnosticsLog log = null)
        {
            var currentNode = qualifiedPath.Root;

            AssertLocalRootExistsInModel(currentNode);
            var traversed = new HashSet <LocalRoot> {
                currentNode
            };
            var paths = new Stack <RelativePath>();

            paths.Push(qualifiedPath.RelativePath);
            if (!PassesFilter(currentNode, paths, log))
            {
                return(null);
            }
            while (graftsByChild.TryGetValue(currentNode, out var graft))
            {
                currentNode = graft.GraftPoint.Root;
                paths.Push(graft.GraftPoint.RelativePath);
                // Sanity check. Should be impossible to configure such a situation.
                if (!traversed.Add(currentNode))
                {
                    throw new InvalidOperationException("Cycle detected in graph.");
                }
                if (!PassesFilter(currentNode, paths, log))
                {
                    return(null);
                }
            }
            foreach (var namedRoot in namedRoots)
            {
                if (namedRoot.LocalRoot != currentNode)
                {
                    continue;
                }
                return(new AbstractPath(namedRoot, RelativePath.Combine(paths)));
            }
            return(null);
        }