1
// ****************************************************************
2
// Copyright 2011, Charlie Poole
3
// This is free software licensed under the NUnit license. You may
4
// obtain a copy of the license at http://nunit.org
5
// ****************************************************************
10
using System.Reflection;
11
using System.Collections.Generic;
12
using System.Runtime.InteropServices;
14
namespace NUnit.ProjectEditor
17
/// Static methods for manipulating doc paths, including both directories
18
/// and files. Some synonyms for System.Path methods are included as well.
20
public class PathUtils
22
public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
23
public const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
24
public const int MAX_PATH = 256;
26
protected static char DirectorySeparatorChar = Path.DirectorySeparatorChar;
27
protected static char AltDirectorySeparatorChar = Path.AltDirectorySeparatorChar;
29
#region Public methods
31
public static bool IsAssemblyFileType( string path )
33
string extension = Path.GetExtension( path ).ToLower();
34
return extension == ".dll" || extension == ".exe";
38
/// Returns the relative path from a base directory to another
39
/// directory or file.
41
public static string RelativePath( string from, string to )
44
throw new ArgumentNullException (from);
46
throw new ArgumentNullException (to);
48
string toPathRoot = Path.GetPathRoot(to);
49
if (toPathRoot == null || toPathRoot == string.Empty)
51
string fromPathRoot = Path.GetPathRoot(from);
53
if (!PathsEqual(toPathRoot, fromPathRoot))
56
string fromNoRoot = from.Substring(fromPathRoot.Length);
57
string toNoRoot = to.Substring(toPathRoot.Length);
59
string[] _from = SplitPath(fromNoRoot);
60
string[] _to = SplitPath(toNoRoot);
62
StringBuilder sb = new StringBuilder (Math.Max (from.Length, to.Length));
64
int last_common, min = Math.Min (_from.Length, _to.Length);
65
for (last_common = 0; last_common < min; ++last_common)
67
if (!PathsEqual(_from[last_common], _to[last_common]))
71
if (last_common < _from.Length)
73
for (int i = last_common + 1; i < _from.Length; ++i)
75
sb.Append (PathUtils.DirectorySeparatorChar).Append ("..");
79
sb.Append (PathUtils.DirectorySeparatorChar);
80
if (last_common < _to.Length)
81
sb.Append (_to [last_common]);
82
for (int i = last_common + 1; i < _to.Length; ++i)
84
sb.Append (PathUtils.DirectorySeparatorChar).Append (_to [i]);
87
return sb.ToString ();
91
/// Return the canonical form of a path.
93
public static string Canonicalize( string path )
95
List<string> parts = new List<string>(
96
path.Split( DirectorySeparatorChar, AltDirectorySeparatorChar ) );
98
for( int index = 0; index < parts.Count; )
100
string part = parts[index];
105
parts.RemoveAt( index );
109
parts.RemoveAt( index );
111
parts.RemoveAt( --index );
119
return String.Join( DirectorySeparatorChar.ToString(), parts.ToArray() );
123
/// True if the two paths are the same. However, two paths
124
/// to the same file or directory using different network
125
/// shares or drive letters are not treated as equal.
127
public static bool SamePath( string path1, string path2 )
129
return string.Compare( Canonicalize(path1), Canonicalize(path2), PathUtils.IsWindows() ) == 0;
133
/// True if the two paths are the same or if the second is
134
/// directly or indirectly under the first. Note that paths
135
/// using different network shares or drive letters are
136
/// considered unrelated, even if they end up referencing
137
/// the same subtrees in the file system.
139
public static bool SamePathOrUnder( string path1, string path2 )
141
path1 = Canonicalize( path1 );
142
path2 = Canonicalize( path2 );
144
int length1 = path1.Length;
145
int length2 = path2.Length;
147
// if path1 is longer, then path2 can't be under it
148
if ( length1 > length2 )
151
// if lengths are the same, check for equality
152
if ( length1 == length2 )
153
return string.Compare( path1, path2, IsWindows() ) == 0;
155
// path 2 is longer than path 1: see if initial parts match
156
if ( string.Compare( path1, path2.Substring( 0, length1 ), IsWindows() ) != 0 )
159
// must match through or up to a directory separator boundary
160
return path2[length1-1] == DirectorySeparatorChar ||
161
path2[length1] == DirectorySeparatorChar;
164
public static string Combine( string path1, params string[] morePaths )
166
string result = path1;
167
foreach( string path in morePaths )
168
result = Path.Combine( result, path );
172
// TODO: This logic should be in shared source
173
public static string GetAssemblyPath( Assembly assembly )
175
string uri = assembly.CodeBase;
177
// If it wasn't loaded locally, use the Location
178
if ( !uri.StartsWith( Uri.UriSchemeFile ) )
179
return assembly.Location;
181
return GetAssemblyPathFromFileUri( uri );
184
// Separate method for testability
185
public static string GetAssemblyPathFromFileUri( string uri )
187
// Skip over the file://
188
int start = Uri.UriSchemeFile.Length + Uri.SchemeDelimiter.Length;
190
if ( PathUtils.DirectorySeparatorChar == '\\' )
192
if ( uri[start] == '/' && uri[start+2] == ':' )
197
if ( uri[start] != '/' )
201
return uri.Substring( start );
205
#region Helper Methods
207
private static bool IsWindows()
209
return PathUtils.DirectorySeparatorChar == '\\';
212
private static string[] SplitPath(string path)
214
char[] separators = new char[] { PathUtils.DirectorySeparatorChar, PathUtils.AltDirectorySeparatorChar };
216
#if CLR_2_0 || CLR_4_0
217
return path.Split(separators, StringSplitOptions.RemoveEmptyEntries);
219
string[] trialSplit = path.Split(separators);
221
int emptyEntries = 0;
222
foreach(string piece in trialSplit)
223
if (piece == string.Empty)
226
if (emptyEntries == 0)
229
string[] finalSplit = new string[trialSplit.Length - emptyEntries];
231
foreach(string piece in trialSplit)
232
if (piece != string.Empty)
233
finalSplit[index++] = piece;
239
private static bool PathsEqual(string path1, string path2)
241
#if CLR_2_0 || CLR_4_0
242
if (PathUtils.IsWindows())
243
return path1.Equals(path2, StringComparison.InvariantCultureIgnoreCase);
245
return path1.Equals(path2, StringComparison.InvariantCulture);
247
if (PathUtils.IsWindows())
248
return path1.ToLower().Equals(path2.ToLower());
250
return path1.Equals(path2);
1
// ****************************************************************
2
// Copyright 2011, Charlie Poole
3
// This is free software licensed under the NUnit license. You may
4
// obtain a copy of the license at http://nunit.org
5
// ****************************************************************
10
using System.Reflection;
11
using System.Collections.Generic;
12
using System.Runtime.InteropServices;
14
namespace NUnit.ProjectEditor
17
/// Static methods for manipulating doc paths, including both directories
18
/// and files. Some synonyms for System.Path methods are included as well.
20
public class PathUtils
22
public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
23
public const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
24
public const int MAX_PATH = 256;
26
protected static char DirectorySeparatorChar = Path.DirectorySeparatorChar;
27
protected static char AltDirectorySeparatorChar = Path.AltDirectorySeparatorChar;
29
#region Public methods
31
public static bool IsAssemblyFileType( string path )
33
string extension = Path.GetExtension( path ).ToLower();
34
return extension == ".dll" || extension == ".exe";
38
/// Returns the relative path from a base directory to another
39
/// directory or file.
41
public static string RelativePath( string from, string to )
44
throw new ArgumentNullException (from);
46
throw new ArgumentNullException (to);
48
string toPathRoot = Path.GetPathRoot(to);
49
if (toPathRoot == null || toPathRoot == string.Empty)
51
string fromPathRoot = Path.GetPathRoot(from);
53
if (!PathsEqual(toPathRoot, fromPathRoot))
56
string fromNoRoot = from.Substring(fromPathRoot.Length);
57
string toNoRoot = to.Substring(toPathRoot.Length);
59
string[] _from = SplitPath(fromNoRoot);
60
string[] _to = SplitPath(toNoRoot);
62
StringBuilder sb = new StringBuilder (Math.Max (from.Length, to.Length));
64
int last_common, min = Math.Min (_from.Length, _to.Length);
65
for (last_common = 0; last_common < min; ++last_common)
67
if (!PathsEqual(_from[last_common], _to[last_common]))
71
if (last_common < _from.Length)
73
for (int i = last_common + 1; i < _from.Length; ++i)
75
sb.Append (PathUtils.DirectorySeparatorChar).Append ("..");
79
sb.Append (PathUtils.DirectorySeparatorChar);
80
if (last_common < _to.Length)
81
sb.Append (_to [last_common]);
82
for (int i = last_common + 1; i < _to.Length; ++i)
84
sb.Append (PathUtils.DirectorySeparatorChar).Append (_to [i]);
87
return sb.ToString ();
91
/// Return the canonical form of a path.
93
public static string Canonicalize( string path )
95
List<string> parts = new List<string>(
96
path.Split( DirectorySeparatorChar, AltDirectorySeparatorChar ) );
98
for( int index = 0; index < parts.Count; )
100
string part = parts[index];
105
parts.RemoveAt( index );
109
parts.RemoveAt( index );
111
parts.RemoveAt( --index );
119
return String.Join( DirectorySeparatorChar.ToString(), parts.ToArray() );
123
/// True if the two paths are the same. However, two paths
124
/// to the same file or directory using different network
125
/// shares or drive letters are not treated as equal.
127
public static bool SamePath( string path1, string path2 )
129
return string.Compare( Canonicalize(path1), Canonicalize(path2), PathUtils.IsWindows() ) == 0;
133
/// True if the two paths are the same or if the second is
134
/// directly or indirectly under the first. Note that paths
135
/// using different network shares or drive letters are
136
/// considered unrelated, even if they end up referencing
137
/// the same subtrees in the file system.
139
public static bool SamePathOrUnder( string path1, string path2 )
141
path1 = Canonicalize( path1 );
142
path2 = Canonicalize( path2 );
144
int length1 = path1.Length;
145
int length2 = path2.Length;
147
// if path1 is longer, then path2 can't be under it
148
if ( length1 > length2 )
151
// if lengths are the same, check for equality
152
if ( length1 == length2 )
153
return string.Compare( path1, path2, IsWindows() ) == 0;
155
// path 2 is longer than path 1: see if initial parts match
156
if ( string.Compare( path1, path2.Substring( 0, length1 ), IsWindows() ) != 0 )
159
// must match through or up to a directory separator boundary
160
return path2[length1-1] == DirectorySeparatorChar ||
161
path2[length1] == DirectorySeparatorChar;
164
public static string Combine( string path1, params string[] morePaths )
166
string result = path1;
167
foreach( string path in morePaths )
168
result = Path.Combine( result, path );
172
// TODO: This logic should be in shared source
173
public static string GetAssemblyPath( Assembly assembly )
175
string uri = assembly.CodeBase;
177
// If it wasn't loaded locally, use the Location
178
if ( !uri.StartsWith( Uri.UriSchemeFile ) )
179
return assembly.Location;
181
return GetAssemblyPathFromFileUri( uri );
184
// Separate method for testability
185
public static string GetAssemblyPathFromFileUri( string uri )
187
// Skip over the file://
188
int start = Uri.UriSchemeFile.Length + Uri.SchemeDelimiter.Length;
190
if ( PathUtils.DirectorySeparatorChar == '\\' )
192
if ( uri[start] == '/' && uri[start+2] == ':' )
197
if ( uri[start] != '/' )
201
return uri.Substring( start );
205
#region Helper Methods
207
private static bool IsWindows()
209
return PathUtils.DirectorySeparatorChar == '\\';
212
private static string[] SplitPath(string path)
214
char[] separators = new char[] { PathUtils.DirectorySeparatorChar, PathUtils.AltDirectorySeparatorChar };
216
#if CLR_2_0 || CLR_4_0
217
return path.Split(separators, StringSplitOptions.RemoveEmptyEntries);
219
string[] trialSplit = path.Split(separators);
221
int emptyEntries = 0;
222
foreach(string piece in trialSplit)
223
if (piece == string.Empty)
226
if (emptyEntries == 0)
229
string[] finalSplit = new string[trialSplit.Length - emptyEntries];
231
foreach(string piece in trialSplit)
232
if (piece != string.Empty)
233
finalSplit[index++] = piece;
239
private static bool PathsEqual(string path1, string path2)
241
#if CLR_2_0 || CLR_4_0
242
if (PathUtils.IsWindows())
243
return path1.Equals(path2, StringComparison.InvariantCultureIgnoreCase);
245
return path1.Equals(path2, StringComparison.InvariantCulture);
247
if (PathUtils.IsWindows())
248
return path1.ToLower().Equals(path2.ToLower());
250
return path1.Equals(path2);