1 // ****************************************************************
2 // Copyright 2002-2003, 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/?p=license&r=2.4
5 // ****************************************************************
10 using System
.Reflection
;
11 using System
.Collections
;
12 using System
.Runtime
.InteropServices
;
17 /// Static methods for manipulating project 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
);
47 if (!Path
.IsPathRooted (to
))
49 if (Path
.GetPathRoot (from) != Path
.GetPathRoot (to
))
52 string[] _from
= from.Split (PathUtils
.DirectorySeparatorChar
,
53 PathUtils
.AltDirectorySeparatorChar
);
54 string[] _to
= to
.Split (PathUtils
.DirectorySeparatorChar
,
55 PathUtils
.AltDirectorySeparatorChar
);
57 StringBuilder sb
= new StringBuilder (Math
.Max (from.Length
, to
.Length
));
59 int last_common
, min
= Math
.Min (_from
.Length
, _to
.Length
);
60 for (last_common
= 0; last_common
< min
; ++last_common
)
62 if (!_from
[last_common
].Equals (_to
[last_common
]))
66 if (last_common
< _from
.Length
)
68 for (int i
= last_common
+ 1; i
< _from
.Length
; ++i
)
70 sb
.Append (PathUtils
.DirectorySeparatorChar
).Append ("..");
74 sb
.Append (PathUtils
.DirectorySeparatorChar
);
75 if (last_common
< _to
.Length
)
76 sb
.Append (_to
[last_common
]);
77 for (int i
= last_common
+ 1; i
< _to
.Length
; ++i
)
79 sb
.Append (PathUtils
.DirectorySeparatorChar
).Append (_to
[i
]);
82 return sb
.ToString ();
86 /// Return the canonical form of a path.
88 public static string Canonicalize( string path
)
90 ArrayList parts
= new ArrayList(
91 path
.Split( DirectorySeparatorChar
, AltDirectorySeparatorChar
) );
93 for( int index
= 0; index
< parts
.Count
; )
95 string part
= (string)parts
[index
];
100 parts
.RemoveAt( index
);
104 parts
.RemoveAt( index
);
106 parts
.RemoveAt( --index
);
114 return String
.Join( DirectorySeparatorChar
.ToString(), (string[])parts
.ToArray( typeof( string ) ) );
118 /// True if the two paths are the same. However, two paths
119 /// to the same file or directory using different network
120 /// shares or drive letters are not treated as equal.
122 public static bool SamePath( string path1
, string path2
)
124 return string.Compare( Canonicalize(path1
), Canonicalize(path2
), PathUtils
.IsWindows() ) == 0;
128 /// True if the two paths are the same or if the second is
129 /// directly or indirectly under the first. Note that paths
130 /// using different network shares or drive letters are
131 /// considered unrelated, even if they end up referencing
132 /// the same subtrees in the file system.
134 public static bool SamePathOrUnder( string path1
, string path2
)
136 path1
= Canonicalize( path1
);
137 path2
= Canonicalize( path2
);
139 int length1
= path1
.Length
;
140 int length2
= path2
.Length
;
142 // if path1 is longer, then path2 can't be under it
143 if ( length1
> length2
)
146 // if lengths are the same, check for equality
147 if ( length1
== length2
)
148 //return path1.ToLower() == path2.ToLower();
149 return string.Compare( path1
, path2
, IsWindows() ) == 0;
151 // path 2 is longer than path 1: see if initial parts match
152 //if ( path1.ToLower() != path2.Substring( 0, length1 ).ToLower() )
153 if ( string.Compare( path1
, path2
.Substring( 0, length1
), IsWindows() ) != 0 )
156 // must match through or up to a directory separator boundary
157 return path2
[length1
-1] == DirectorySeparatorChar
||
158 path2
[length1
] == DirectorySeparatorChar
;
161 public static string Combine( string path1
, params string[] morePaths
)
163 string result
= path1
;
164 foreach( string path
in morePaths
)
165 result
= Path
.Combine( result
, path
);
169 // TODO: This logic should be in shared source
170 public static string GetAssemblyPath( Assembly assembly
)
172 string uri
= assembly
.CodeBase
;
174 // If it wasn't loaded locally, use the Location
175 if ( !uri
.StartsWith( Uri
.UriSchemeFile
) )
176 return assembly
.Location
;
178 return GetAssemblyPathFromFileUri( uri
);
181 // Separate method for testability
182 public static string GetAssemblyPathFromFileUri( string uri
)
184 // Skip over the file://
185 int start
= Uri
.UriSchemeFile
.Length
+ Uri
.SchemeDelimiter
.Length
;
187 if ( PathUtils
.DirectorySeparatorChar
== '\\' )
189 if ( uri
[start
] == '/' && uri
[start
+2] == ':' )
194 if ( uri
[start
] != '/' )
198 return uri
.Substring( start
);
202 #region Helper Methods
203 private static bool IsWindows()
205 return PathUtils
.DirectorySeparatorChar
== '\\';