Created
January 4, 2011 15:26
-
-
Save grumpydev/764902 to your computer and use it in GitHub Desktop.
Helper class to try to convert an absolute path to one relative to another path
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using System.Text; | |
| using System.IO; | |
| namespace AbsoluteToRelativePathTest | |
| { | |
| public static class PathHelper | |
| { | |
| /// <summary> | |
| /// Attempts to convert an absolute path (with or without filename) to a path relative to another path. | |
| /// | |
| /// e.g. GetRelativePathIfPossible(@"c:\testdir1\testdir2\", @"c:\testdir1\testdir3\test.doc") will return @"..\testdir3\test.doc" | |
| /// </summary> | |
| /// <param name="relativeToDirectoryPath">Absolute path to calculate the relative path from</param> | |
| /// <param name="absolutePath">Absolute path (with or without a filename) to the file/directory</param> | |
| /// <param name="ignoreCase">Whether to ignore case when comparing directories</param> | |
| /// <returns>A relative path, or absolutePath if a relative coversion isn't possible.</returns> | |
| public static string GetRelativePathIfPossible(string relativeToDirectoryPath, string absolutePath, bool ignoreCase = true) | |
| { | |
| if (Path.GetFileName(relativeToDirectoryPath) != String.Empty) | |
| throw new ArgumentException("relativeToDirectoryPath must be a directory name, not a filename"); | |
| if (!Path.IsPathRooted(relativeToDirectoryPath)) | |
| throw new ArgumentException("relativeToDirectoryPath must be an absolute path!"); | |
| if (!Path.IsPathRooted(absolutePath)) | |
| throw new ArgumentException("absolutePath must be an absolute path!"); | |
| var relativeToDirectory = relativeToDirectoryPath.TrimEnd('\\'); | |
| var absolutePathDirectory = Path.GetDirectoryName(absolutePath).TrimEnd('\\'); | |
| var absoluteFileName = Path.GetFileName(absolutePath); | |
| if (string.Equals(absolutePathDirectory, relativeToDirectory, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) | |
| return absoluteFileName; | |
| if (Path.GetPathRoot(relativeToDirectoryPath) != Path.GetPathRoot(absolutePath)) | |
| return absolutePath; | |
| var relativeToDirectories = relativeToDirectory.Split(Path.DirectorySeparatorChar); | |
| var absolutePathDirectories = absolutePathDirectory.Split(Path.DirectorySeparatorChar); | |
| var commonPathCount = GetCommonPathCount(relativeToDirectories, absolutePathDirectories, ignoreCase); | |
| if (commonPathCount == 0) | |
| return absolutePath; | |
| var remainingRelativeToDirectories = relativeToDirectories.Skip(commonPathCount); | |
| var remainingAbsolutePathDirectories = absolutePathDirectories.Skip(commonPathCount); | |
| var pathBuilder = new StringBuilder(); | |
| BuildParentDirectories(remainingRelativeToDirectories, pathBuilder); | |
| BuildSubDirectories(remainingAbsolutePathDirectories, pathBuilder); | |
| pathBuilder.Append(absoluteFileName); | |
| return pathBuilder.ToString(); | |
| } | |
| private static int GetCommonPathCount(string[] path1Directories, string[] path2Directories, bool ignoreCase) | |
| { | |
| int minLength = Math.Min(path1Directories.Length, path2Directories.Length); | |
| bool isMatching = true; | |
| var matchingDirectoryCount = 0; | |
| for (int index = 0; index < minLength && isMatching; index++) | |
| { | |
| if (String.Equals(path1Directories[index], path2Directories[index], ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) | |
| matchingDirectoryCount++; | |
| else | |
| isMatching = false; | |
| } | |
| return matchingDirectoryCount; | |
| } | |
| private static void BuildParentDirectories(IEnumerable<string> remainingRelativeToDirectories, StringBuilder pathBuilder) | |
| { | |
| for (int index = 0; index < remainingRelativeToDirectories.Count(); index++) | |
| { | |
| pathBuilder.Append(".."); | |
| pathBuilder.Append(Path.DirectorySeparatorChar); | |
| } | |
| } | |
| private static void BuildSubDirectories(IEnumerable<string> remainingAbsolutePathDirectories, StringBuilder pathBuilder) | |
| { | |
| foreach (var directory in remainingAbsolutePathDirectories) | |
| { | |
| pathBuilder.Append(directory); | |
| pathBuilder.Append(Path.DirectorySeparatorChar); | |
| } | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using System.Text; | |
| using Microsoft.VisualStudio.TestTools.UnitTesting; | |
| namespace AbsoluteToRelativePathTest.Tests | |
| { | |
| [TestClass] | |
| public class PathHelperTests | |
| { | |
| [TestMethod] | |
| public void GetRelativePathIfPossible_RelativeToRelativePath_ThrowsArgumentException() | |
| { | |
| try | |
| { | |
| var relativeTo = @"..\"; | |
| var absolutePath = @"c:\testdir\testdir2\test2.doc"; | |
| var relativePath = PathHelper.GetRelativePathIfPossible(relativeTo, absolutePath); | |
| Assert.Fail("Should have thrown"); | |
| } | |
| catch (ArgumentException) | |
| { | |
| } | |
| } | |
| [TestMethod] | |
| public void GetRelativePathIfPossible_AbsolutePathRelativePath_ThrowsArgumentException() | |
| { | |
| try | |
| { | |
| var relativeTo = @"c:\testdir\testdir2\filename.doc"; | |
| var absolutePath = @"..\test.doc"; | |
| var relativePath = PathHelper.GetRelativePathIfPossible(relativeTo, absolutePath); | |
| Assert.Fail("Should have thrown"); | |
| } | |
| catch (ArgumentException) | |
| { | |
| } | |
| } | |
| [TestMethod] | |
| public void GetRelativePathIfPossible_RelativeToPathWithFilename_ThrowsArgumentException() | |
| { | |
| try | |
| { | |
| var relativeTo = @"c:\testdir\testdir2\filename.doc"; | |
| var absolutePath = @"c:\testdir\testdir2\test2.doc"; | |
| var relativePath = PathHelper.GetRelativePathIfPossible(relativeTo, absolutePath); | |
| Assert.Fail("Should have thrown"); | |
| } | |
| catch (ArgumentException) | |
| { | |
| } | |
| } | |
| [TestMethod] | |
| public void GetRelativePathIfPossible_SameDirectoryAndFilename_ReturnsJustFileName() | |
| { | |
| var relativeTo = @"c:\testdir\testdir2\"; | |
| var absolutePath = @"c:\testdir\testdir2\test2.doc"; | |
| var relativePath = PathHelper.GetRelativePathIfPossible(relativeTo, absolutePath); | |
| Assert.AreEqual(@"test2.doc", relativePath); | |
| } | |
| [TestMethod] | |
| public void GetRelativePathIfPossible_SameDirectoryNoFilename_ReturnsBlank() | |
| { | |
| var relativeTo = @"c:\testdir\testdIr2\"; | |
| var absolutePath = @"c:\testdir\testdir2\"; | |
| var relativePath = PathHelper.GetRelativePathIfPossible(relativeTo, absolutePath); | |
| Assert.AreEqual(@"", relativePath); | |
| } | |
| [TestMethod] | |
| public void GetRelativePathIfPossible_DifferentDrives_ReturnsAbsolute() | |
| { | |
| var relativeTo = @"c:\testdir\"; | |
| var absolutePath = @"d:\testdir"; | |
| var relativePath = PathHelper.GetRelativePathIfPossible(relativeTo, absolutePath); | |
| Assert.AreEqual(absolutePath, relativePath); | |
| } | |
| [TestMethod] | |
| public void GetRelativePathIfPossible_OneDirectoryUp_ReturnsDoubleDotAndFilename() | |
| { | |
| var relativeTo = @"c:\testdir\testdir2\"; | |
| var absolutePath = @"c:\testdir\test2.doc"; | |
| var relativePath = PathHelper.GetRelativePathIfPossible(relativeTo, absolutePath); | |
| Assert.AreEqual(@"..\test2.doc", relativePath); | |
| } | |
| [TestMethod] | |
| public void GetRelativePathIfPossible_TwoDirectoriesUp_ReturnsTwoDoubleDotAndFilename() | |
| { | |
| var relativeTo = @"c:\testdir\testdir2\"; | |
| var absolutePath = @"c:\test2.doc"; | |
| var relativePath = PathHelper.GetRelativePathIfPossible(relativeTo, absolutePath); | |
| Assert.AreEqual(@"..\..\test2.doc", relativePath); | |
| } | |
| [TestMethod] | |
| public void GetRelativePathIfPossible_OneDirectoryDeep_ReturnsDirectoryAndFilename() | |
| { | |
| var relativeTo = @"c:\testdir\testdir2\"; | |
| var absolutePath = @"c:\testdir\test2.doc"; | |
| var relativePath = PathHelper.GetRelativePathIfPossible(relativeTo, absolutePath); | |
| Assert.AreEqual(@"..\test2.doc", relativePath); | |
| } | |
| [TestMethod] | |
| public void GetRelativePathIfPossible_TwoDirectoriesUpAndOneDirectoryDeep_ReturnsTwoDoubleDotAndSubdirAndFilename() | |
| { | |
| var relativeTo = @"c:\testdir1\testdir2\"; | |
| var absolutePath = @"c:\testdir1\testdir3\test.doc"; | |
| var relativePath = PathHelper.GetRelativePathIfPossible(relativeTo, absolutePath); | |
| Assert.AreEqual(@"..\testdir3\test.doc", relativePath); | |
| } | |
| [TestMethod] | |
| public void GetRelativePathIfPossible_TwoDirectoriesUpAndTwoDirectoriesDeep_ReturnsTwoDoubleDotAndSubdirsAndFilename() | |
| { | |
| var relativeTo = @"c:\a\b\c\testdir1\testdir2\"; | |
| var absolutePath = @"c:\a\b\c\testdir1\testdir3\testdir4\test.doc"; | |
| var relativePath = PathHelper.GetRelativePathIfPossible(relativeTo, absolutePath); | |
| Assert.AreEqual(@"..\testdir3\testdir4\test.doc", relativePath); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment