/// <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); }
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); }