PageRenderTime 19ms CodeModel.GetById 4ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/src/NUnit/util/PathUtils.cs

#
C# | 260 lines | 178 code | 45 blank | 37 comment | 52 complexity | 3521133c9c8f32d0fe34c8004478c094 MD5 | raw file
  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
  5// ****************************************************************
  6
  7using System;
  8using System.IO;
  9using System.Text;
 10using System.Reflection;
 11using System.Collections;
 12using System.Runtime.InteropServices;
 13
 14namespace NUnit.Util
 15{
 16	/// <summary>
 17	/// Static methods for manipulating project paths, including both directories
 18	/// and files. Some synonyms for System.Path methods are included as well.
 19	/// </summary> 
 20	public class PathUtils
 21	{
 22		public const uint FILE_ATTRIBUTE_DIRECTORY  = 0x00000010;  
 23		public const uint FILE_ATTRIBUTE_NORMAL     = 0x00000080;  
 24		public const int MAX_PATH = 256;
 25
 26		protected static char DirectorySeparatorChar = Path.DirectorySeparatorChar;
 27		protected static char AltDirectorySeparatorChar = Path.AltDirectorySeparatorChar;
 28
 29		#region Public methods
 30
 31		public static bool IsAssemblyFileType( string path )
 32		{
 33			string extension = Path.GetExtension( path ).ToLower();
 34			return extension == ".dll" || extension == ".exe";
 35		}
 36
 37		/// <summary>
 38		/// Returns the relative path from a base directory to another
 39		/// directory or file.
 40		/// </summary>
 41		public static string RelativePath( string from, string to )
 42		{
 43			if (from == null)
 44				throw new ArgumentNullException (from);
 45			if (to == null)
 46				throw new ArgumentNullException (to);
 47
 48            string toPathRoot = Path.GetPathRoot(to);
 49            if (toPathRoot == null || toPathRoot == string.Empty)
 50                return to;
 51            string fromPathRoot = Path.GetPathRoot(from);
 52
 53            if (!PathsEqual(toPathRoot, fromPathRoot))
 54                return null;
 55
 56            string fromNoRoot = from.Substring(fromPathRoot.Length);
 57            string toNoRoot = to.Substring(toPathRoot.Length);
 58
 59            string[] _from = SplitPath(fromNoRoot);
 60            string[] _to = SplitPath(toNoRoot);
 61
 62			StringBuilder sb = new StringBuilder (Math.Max (from.Length, to.Length));
 63
 64			int last_common, min = Math.Min (_from.Length, _to.Length);
 65			for (last_common = 0; last_common < min;  ++last_common) 
 66			{
 67                if (!PathsEqual(_from[last_common], _to[last_common]))
 68                    break;
 69            }
 70
 71			if (last_common < _from.Length)
 72				sb.Append ("..");
 73			for (int i = last_common + 1; i < _from.Length; ++i) 
 74			{
 75				sb.Append (PathUtils.DirectorySeparatorChar).Append ("..");
 76			}
 77
 78			if (sb.Length > 0)
 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) 
 83			{
 84				sb.Append (PathUtils.DirectorySeparatorChar).Append (_to [i]);
 85			}
 86
 87			return sb.ToString ();
 88		}
 89
 90		/// <summary>
 91		/// Return the canonical form of a path.
 92		/// </summary>
 93		public static string Canonicalize( string path )
 94		{
 95			ArrayList parts = new ArrayList(
 96				path.Split( DirectorySeparatorChar, AltDirectorySeparatorChar ) );
 97
 98			for( int index = 0; index < parts.Count; )
 99			{
100				string part = (string)parts[index];
101		
102				switch( part )
103				{
104					case ".":
105						parts.RemoveAt( index );
106						break;
107				
108					case "..":
109						parts.RemoveAt( index );
110						if ( index > 0 )
111							parts.RemoveAt( --index );
112						break;
113					default:
114						index++;
115						break;
116				}
117			}
118
119            // Trailing separator removal
120            if (parts.Count > 1 && path.Length > 1 && (string)parts[parts.Count - 1] == "")
121                parts.RemoveAt(parts.Count - 1);
122
123            return String.Join(DirectorySeparatorChar.ToString(), (string[])parts.ToArray(typeof(string)));
124		}
125
126		/// <summary>
127		/// True if the two paths are the same. However, two paths
128		/// to the same file or directory using different network
129		/// shares or drive letters are not treated as equal.
130		/// </summary>
131		public static bool SamePath( string path1, string path2 )
132		{
133			return string.Compare( Canonicalize(path1), Canonicalize(path2), PathUtils.IsWindows() ) == 0;
134		}
135
136		/// <summary>
137		/// True if the two paths are the same or if the second is
138		/// directly or indirectly under the first. Note that paths 
139		/// using different network shares or drive letters are 
140		/// considered unrelated, even if they end up referencing
141		/// the same subtrees in the file system.
142		/// </summary>
143		public static bool SamePathOrUnder( string path1, string path2 )
144		{
145			path1 = Canonicalize( path1 );
146			path2 = Canonicalize( path2 );
147
148			int length1 = path1.Length;
149			int length2 = path2.Length;
150
151			// if path1 is longer, then path2 can't be under it
152			if ( length1 > length2 )
153				return false;
154
155			// if lengths are the same, check for equality
156			if ( length1 == length2 )
157				return string.Compare( path1, path2, IsWindows() ) == 0;
158
159			// path 2 is longer than path 1: see if initial parts match
160			if ( string.Compare( path1, path2.Substring( 0, length1 ), IsWindows() ) != 0 )
161				return false;
162			
163			// must match through or up to a directory separator boundary
164			return	path2[length1-1] == DirectorySeparatorChar ||
165				path2[length1] == DirectorySeparatorChar;
166		}
167
168		public static string Combine( string path1, params string[] morePaths )
169		{
170			string result = path1;
171			foreach( string path in morePaths )
172				result = Path.Combine( result, path );
173			return result;
174		}
175
176		// TODO: This logic should be in shared source
177		public static string GetAssemblyPath( Assembly assembly )
178		{
179			string uri = assembly.CodeBase;
180
181			// If it wasn't loaded locally, use the Location
182			if ( !uri.StartsWith( Uri.UriSchemeFile ) )
183				return assembly.Location;
184
185			return GetAssemblyPathFromFileUri( uri );
186		}
187
188		// Separate method for testability
189		public static string GetAssemblyPathFromFileUri( string uri )
190		{
191			// Skip over the file://
192			int start = Uri.UriSchemeFile.Length + Uri.SchemeDelimiter.Length;
193			
194			if ( PathUtils.DirectorySeparatorChar == '\\' )
195			{
196				if ( uri[start] == '/' && uri[start+2] == ':' )
197					++start;
198			}
199			else
200			{
201				if ( uri[start] != '/' )
202					--start;
203			}
204
205			return uri.Substring( start );
206		}
207		#endregion
208
209		#region Helper Methods
210
211		private static bool IsWindows()
212		{
213			return PathUtils.DirectorySeparatorChar == '\\';
214		}
215
216        private static string[] SplitPath(string path)
217        {
218            char[] separators = new char[] { PathUtils.DirectorySeparatorChar, PathUtils.AltDirectorySeparatorChar };
219
220#if NET_2_0
221            return path.Split(separators, StringSplitOptions.RemoveEmptyEntries);
222#else
223            string[] trialSplit = path.Split(separators);
224            
225            int emptyEntries = 0;
226            foreach(string piece in trialSplit)
227                if (piece == string.Empty)
228                    emptyEntries++;
229
230            if (emptyEntries == 0)
231                return trialSplit;
232
233            string[] finalSplit = new string[trialSplit.Length - emptyEntries];
234            int index = 0;
235            foreach(string piece in trialSplit)
236                if (piece != string.Empty)
237                    finalSplit[index++] = piece;
238
239            return finalSplit;
240#endif
241        }
242
243        private static bool PathsEqual(string path1, string path2)
244        {
245#if NET_2_0
246            if (PathUtils.IsWindows())
247                return path1.Equals(path2, StringComparison.InvariantCultureIgnoreCase);
248            else
249                return path1.Equals(path2, StringComparison.InvariantCulture);
250#else
251            if (PathUtils.IsWindows())
252                return path1.ToLower().Equals(path2.ToLower());
253            else
254                return path1.Equals(path2);
255#endif
256        }
257
258        #endregion
259	}
260}