Relative Paths
/* * Copyright (c) United Binary LLC. All rights reserved. * * This code is licensed under the MIT License * * SEE: http://harnessit.codeplex.com/license * */ #region using ... using System; using System.ComponentModel; using System.Diagnostics; using System.Text; #endregion namespace UnitedBinary.Core.IO { /// <include file='IO.xml' path='/Docs/RelativePaths/Class/*'/> public class RelativePaths { private string basepath = string.Empty; /// <include file='IO.xml' path='/Docs/RelativePaths/ctor_string/*'/> public RelativePaths(string basePath) { if (basePath == null || basePath.Length == 0) { throw new ArgumentException( "Invalid base path." ); } BasePath = basePath; } /// <include file='IO.xml' path='/Docs/RelativePaths/ctor/*'/> public RelativePaths() { } /// <include file='IO.xml' path='/Docs/RelativePaths/GetRelativePath/*'/> public string GetRelativePath(string fullPath) { if (fullPath == null) { throw new ArgumentNullException( "fullPath" ); } bool isCommonPath = CheckForCommonDrive( fullPath ); bool isCommonNetwork = CheckForCommonNetworkPath( fullPath ); if (!isCommonPath && !isCommonNetwork) { return fullPath; } char[] delims = {'\\'}; string[] baseArray = BasePath.Split( delims ); string[] fullArray = fullPath.Split( delims ); int divergenceIndex = 0; for (int i=0; i < System.Math.Min( baseArray.Length, fullArray.Length ); i++) { if (string.Compare(baseArray[i], fullArray[i], true) != 0) { divergenceIndex = i; break; } } if (divergenceIndex == 0) { divergenceIndex = baseArray.Length; } int upCount = baseArray.Length - divergenceIndex; string relPath = string.Empty; for (int i=0; i < upCount; i++) { relPath += "..\\"; } for (int i=divergenceIndex; i < fullArray.Length; i++) { relPath += fullArray[i]; if (i < fullArray.Length-1) { relPath += "\\"; } } Debug.WriteLine( "Divergence at index " + divergenceIndex ); Debug.WriteLine( "Full path: " + fullPath ); Debug.WriteLine( "Base path: " + BasePath ); Debug.WriteLine( "Generated relative path: " + relPath ); return relPath; } private bool CheckForCommonNetworkPath(string fullPath) { if (fullPath.Length == 0) { return false; } string basePath = BasePath; bool isBaseNetwork = basePath.StartsWith( @"\\" ); bool isFullNetwork = fullPath.StartsWith( @"\\" ); bool bothAreNetworks = isBaseNetwork && isFullNetwork; if (!bothAreNetworks) { return false; } int nextSlash = basePath.IndexOf( '\\', 2 ); if (nextSlash < 0) { return false; } string commonRoot = basePath.Substring( 0, nextSlash + 1 ); bool commonNetwork = string.Compare( basePath, 0, fullPath, 0, nextSlash+1, true ) == 0; return commonNetwork; } private bool CheckForCommonDrive(string fullPath) { if (fullPath.Length == 0) { return false; } string basePath = BasePath; bool isBaseDrive = basePath[1] == ':'; bool isFullDrive = fullPath[1] == ':'; bool bothAreDrives = isBaseDrive && isFullDrive; bool commonDrives = bothAreDrives && char.ToLower(basePath[0]) == char.ToLower( fullPath[0] ); if (bothAreDrives) { return commonDrives; } return false; } /// <include file='IO.xml' path='/Docs/RelativePaths/BasePath/*'/> public string BasePath { get { return basepath; } set { if (value == null) { throw new ArgumentNullException( "value" ); } string startingSlashes = GetPrefixedSlashes( value.Trim() ); basepath = value.Trim( new char[] {' ', '\\'} ); basepath = startingSlashes + basepath; } } private string GetPrefixedSlashes(string value) { StringBuilder sb = new StringBuilder(); for (int i=0; i < value.Length; i++) { if (value[i] != '\\') { break; } else { sb.Append( '\\' ); } } string startingSlashes = sb.ToString(); return startingSlashes; } /// <include file='IO.xml' path='/Docs/Empty/Empty/*'/> [EditorBrowsable(EditorBrowsableState.Never)] public string DirectoryFromFileName(string fullPathName) { if (fullPathName == null || fullPathName.Length == 0) { throw new ArgumentException( "Invalid FullPathName." ); } int dirIndex = fullPathName.Length; for (int i=fullPathName.Length-1; i > 0; i--) { if (fullPathName[i] == '\\') { dirIndex = i; break; } } if (dirIndex == fullPathName.Length) { return fullPathName; } string dir = fullPathName.Remove( dirIndex, fullPathName.Length - dirIndex ); char[] trimmer = {'\\',' '}; dir = dir.Trim( trimmer ); Debug.WriteLine( "DirectoryFromFileName: Converted-> " + fullPathName ); Debug.WriteLine( "DirectoryFromFileName: To--------> " + dir ); return dir; } /// <include file='IO.xml' path='/Docs/RelativePaths/RebuildFullPath/*'/> public string RebuildFullPath(string relativeFilePath) { if (relativeFilePath == null || relativeFilePath.Length < 2) { return this.BasePath; } // // Check some special cases that don't fit the algorithm. // 1. The file is on a different drive than base (nothing relative to work with). // 2. The base path is a relative path itself. // if (relativeFilePath[1] == ':') { Debug.WriteLine( "Relative path contains drive letter - it is already a full path. Returning." ); return relativeFilePath; } else if (relativeFilePath.StartsWith(@"\\")) { Debug.WriteLine( "Relative path is a newtwork share - it is already a full path. Returning." ); return relativeFilePath; } if (BasePath.StartsWith("..")) { Debug.WriteLine( "Base is relative path, concatenating." ); return BasePath + "\\" + relativeFilePath; } char[] splitter = {'\\'}; string[] dirs = relativeFilePath.Split( splitter ); int upCount = 0; foreach (string dir in dirs) { if (dir == "..") { upCount++; } } Debug.WriteLine( "Up count = " + upCount ); StringBuilder sbFullPath2 = new StringBuilder(); for (int i=upCount; i < dirs.Length; i++) { sbFullPath2.Append("\\").Append( dirs[i] ); } string fullPath2 = sbFullPath2.ToString(); Debug.WriteLine( "FullPath2 = " + fullPath2 ); dirs = BasePath.Split( splitter ); StringBuilder sbFullPath1 = new StringBuilder(); sbFullPath1.Append( dirs[0] ); for (int i=1; i < dirs.Length-upCount; i++) { sbFullPath1.Append("\\").Append( dirs[i] ); } string fullPath1 = sbFullPath1.ToString(); Debug.WriteLine( "FullPath1 = " + fullPath1 ); string fullPath = fullPath1 + fullPath2; Debug.WriteLine( "FullPath = " + fullPath ); return fullPath; } } }