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;
}
}
}
Related examples in the same category