2009-06-12 Bill Holmes <billholmes54@gmail.com>
[mono.git] / mcs / nunit20 / util / ProjectPath.cs
1 #region Copyright (c) 2002-2003, James W. Newkirk, Michael C. Two, Alexei A. Vorontsov, Charlie Poole, Philip A. Craig
2 /************************************************************************************
3 '
4 ' Copyright  2002-2003 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov, Charlie Poole
5 ' Copyright  2000-2002 Philip A. Craig
6 '
7 ' This software is provided 'as-is', without any express or implied warranty. In no 
8 ' event will the authors be held liable for any damages arising from the use of this 
9 ' software.
10
11 ' Permission is granted to anyone to use this software for any purpose, including 
12 ' commercial applications, and to alter it and redistribute it freely, subject to the 
13 ' following restrictions:
14 '
15 ' 1. The origin of this software must not be misrepresented; you must not claim that 
16 ' you wrote the original software. If you use this software in a product, an 
17 ' acknowledgment (see the following) in the product documentation is required.
18 '
19 ' Portions Copyright  2002-2003 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov, Charlie Poole
20 ' or Copyright  2000-2002 Philip A. Craig
21 '
22 ' 2. Altered source versions must be plainly marked as such, and must not be 
23 ' misrepresented as being the original software.
24 '
25 ' 3. This notice may not be removed or altered from any source distribution.
26 '
27 '***********************************************************************************/
28 #endregion
29
30 using System;
31 using System.IO;
32 using System.Text;
33 using System.Runtime.InteropServices;
34 #if TARGET_JVM
35 using System.Collections;
36 #endif
37 namespace NUnit.Util
38 {
39         /// <summary>
40         /// Static methods for manipulating project paths, including both directories
41         /// and files. Some synonyms for System.Path methods are included as well.
42         /// </summary>
43         public class ProjectPath
44         {
45                 public const uint FILE_ATTRIBUTE_DIRECTORY  = 0x00000010;  
46                 public const uint FILE_ATTRIBUTE_NORMAL     = 0x00000080;  
47                 public const int MAX_PATH = 256;
48
49                 #region Public methods
50
51                 public static bool IsAssemblyFileType( string path )
52                 {
53                         string extension = Path.GetExtension( path );
54                         return extension == ".dll" || extension == ".exe";
55                 }
56
57 #if !TARGET_JVM
58                 /// <summary>
59                 /// Returns the relative path from a base directory to another
60                 /// directory or file.
61                 /// </summary>
62                 public static string RelativePath( string from, string to )
63                 {
64                         from = Canonicalize( from );
65                         to = Canonicalize( to );
66
67                         // Second argument to PathRelativeTo must be absolute
68                         if ( !Path.IsPathRooted( to ) )
69                                 return to;
70                         
71                         StringBuilder sb = new StringBuilder( MAX_PATH );
72
73                         // Return null if call fails
74                         if ( !PathRelativePathTo( sb, from, FILE_ATTRIBUTE_DIRECTORY, to, FILE_ATTRIBUTE_DIRECTORY ) )
75                                 return null;
76
77                         // Remove initial .\ from path if present
78                         if ( sb.Length >=2 && sb[0] == '.' && sb[1] == '\\' )
79                                 sb.Remove( 0, 2 );
80
81                         if ( sb.Length == 0 )
82                                 return null;
83
84                         return sb.ToString();
85                 }
86 #else
87                 /// <summary>
88                 /// Returns the relative path from a base directory to another
89                 /// directory or file.
90                 /// </summary>
91                 public static string RelativePath( string from, string to )
92                 {
93                         char dirSeperator = System.IO.Path.DirectorySeparatorChar;
94                         string upDirStr = @"..\";
95
96                         //Start by normalizing paths
97                         NormalizePath (ref from);
98                         NormalizePath (ref to);
99
100                         if ( !IsPathValid (from) || !IsPathValid (to) )
101                                 return string.Empty;
102
103                         if (!System.IO.Path.IsPathRooted (to))
104                                 return to;
105
106                         //First check if FullPath begins with the BasePath
107                         if ( to.StartsWith (from)) 
108                                 return  to.Replace (from,".");
109
110                         //Now parse backwards
111                         StringBuilder backDirs = new StringBuilder ();
112                         string partialPath = from;
113                         int index = partialPath.LastIndexOf (dirSeperator);
114                         while (index > 0) {
115                                 //Strip path step string to last backslash and add another step backwards to our pass replacement
116                                 partialPath = partialPath.Substring(0,index);
117                                 backDirs.Append(upDirStr);
118
119                                 //check if FullPath begins with the current partialPath
120                                 if ( to.StartsWith(partialPath) )
121                                         if ( to == partialPath ) {
122                                                 //Full Directory match and need to replace it all
123                                                 backDirs.Remove (backDirs.Length-1, 1);
124                                                 return backDirs.ToString ();
125                                         }
126                                         else //We're dealing with a file or a start path
127                                                 return to.Replace (partialPath + dirSeperator, backDirs.ToString ());
128
129                                 index = partialPath.LastIndexOf (dirSeperator, partialPath.Length-1);
130                         }                       
131                         //No common root found, return null.
132                         return null;
133                 }
134 #endif
135                 /// <summary>
136                 /// Return the canonical form of a path.
137                 public static string Canonicalize( string path )
138                 {
139 #if !TARGET_JVM
140                         StringBuilder sb = new StringBuilder( MAX_PATH );
141                         if ( !PathCanonicalize( sb, path ) )
142                                 throw new ArgumentException( string.Format( "Invalid path passed to PathCanonicalize: {0}", path ) );
143
144                         return sb.ToString();
145 #else
146                         return Path.GetFullPath( path );
147 #endif
148                 }
149
150                 /// <summary>
151                 /// True if the two paths are the same. However, two paths
152                 /// to the same file or directory using different network
153                 /// shares or drive letters are not treated as equal.
154                 /// </summary>
155                 public static bool SamePath( string path1, string path2 )
156                 {
157                         return Canonicalize(path1).ToLower() == Canonicalize(path2).ToLower();
158                 }
159
160                 /// <summary>
161                 /// True if the two paths are the same or if the second is
162                 /// directly or indirectly under the first. Note that paths 
163                 /// using different network shares or drive letters are 
164                 /// considered unrelated, even if they end up referencing
165                 /// the same subtrees in the file system.
166                 /// </summary>
167                 public static bool SamePathOrUnder( string path1, string path2 )
168                 {
169                         path1 = Canonicalize( path1 );
170                         path2 = Canonicalize( path2 );
171
172                         int length1 = path1.Length;
173                         int length2 = path2.Length;
174
175                         // if path1 is longer, then path2 can't be under it
176                         if ( length1 > length2 )
177                                 return false;
178
179                         // if lengths are the same, check for equality
180                         if ( length1 == length2 )
181                                 return path1.ToLower() == path2.ToLower();
182
183                         // path 2 is longer than path 1: see if initial parts match
184                         if ( path1.ToLower() != path2.Substring( 0, length1 ).ToLower() )
185                                 return false;
186                         
187                         // must match through or up to a directory separator boundary
188                         return  path2[length1-1] == Path.DirectorySeparatorChar ||
189                                         path2[length1] == Path.DirectorySeparatorChar;
190                 }
191
192                 #endregion
193
194 #if !TARGET_JVM
195                 #region Shlwapi functions used internally
196
197                 [DllImport("shlwapi.dll")]
198                 private static extern bool PathRelativePathTo(
199                         StringBuilder result,
200                         string from,
201                         uint attrFrom,
202                         string to,
203                         uint attrTo );
204
205                 [DllImport("shlwapi.dll")]
206                 private static extern bool PathCanonicalize(
207                         StringBuilder result,
208                         string path );
209
210                 [DllImport("shlwapi.dll")]
211                 private static extern int PathCommonPrefix(
212                         string file1,
213                         string file2,
214                         StringBuilder result );
215                         
216                 #endregion
217 #else
218                 private static bool IsPathValid(string path)
219                 {
220                         return (path!=string.Empty) && (path[0] != System.IO.Path.DirectorySeparatorChar);
221                 }
222
223                 private static void NormalizePath (ref string path)
224                 {
225                         string dirSeperator = new string (new char [] {System.IO.Path.DirectorySeparatorChar});
226
227                         path = path.ToLower ();
228                         if (path.EndsWith (dirSeperator))
229                                 path = path.Substring (0, path.Length - 1);
230                 }
231
232 #endif
233         }
234 }