Merge pull request #980 from StephenMcConnel/bug-18638
[mono.git] / mcs / class / Microsoft.Build / Microsoft.Build.Evaluation / ProjectCollection.cs
1 //
2 // ProjectCollection.cs
3 //
4 // Author:
5 //   Leszek Ciesielski (skolima@gmail.com)
6 //   Rolf Bjarne Kvinge (rolf@xamarin.com)
7 //   Atsushi Enomoto (atsushi@xamarin.com)
8 //
9 // (C) 2011 Leszek Ciesielski
10 // Copyright (C) 2011,2013 Xamarin Inc.
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using Microsoft.Build.Construction;
33 using Microsoft.Build.Execution;
34 using Microsoft.Build.Framework;
35 using Microsoft.Build.Logging;
36 using Microsoft.Build.Utilities;
37 using System;
38 using System.Collections.Generic;
39 using System.Collections.ObjectModel;
40 using System.IO;
41 using System.Linq;
42 using System.Xml;
43 using System.Reflection;
44 using System.Globalization;
45 using Mono.XBuild.Utilities;
46 using Microsoft.Build.Internal;
47
48 namespace Microsoft.Build.Evaluation
49 {
50         public class ProjectCollection : IDisposable
51         {
52                 public delegate void ProjectAddedEventHandler (object target, ProjectAddedToProjectCollectionEventArgs args);
53                 
54                 public class ProjectAddedToProjectCollectionEventArgs : EventArgs
55                 {
56                         public ProjectAddedToProjectCollectionEventArgs (ProjectRootElement project)
57                         {
58                                 if (project == null)
59                                         throw new ArgumentNullException ("project");
60                                 ProjectRootElement = project;
61                         }
62                         
63                         public ProjectRootElement ProjectRootElement { get; private set; }
64                 }
65
66                 // static members
67
68                 static readonly ProjectCollection global_project_collection;
69
70                 static ProjectCollection ()
71                 {
72                         #if NET_4_5
73                         global_project_collection = new ProjectCollection (new ReadOnlyDictionary<string, string> (new Dictionary<string, string> ()));
74                         #else
75                         global_project_collection = new ProjectCollection (new Dictionary<string, string> ());
76                         #endif
77                 }
78
79                 public static string Escape (string unescapedString)
80                 {
81                         return Mono.XBuild.Utilities.MSBuildUtils.Escape (unescapedString);
82                 }
83
84                 public static string Unescape (string escapedString)
85                 {
86                         return Mono.XBuild.Utilities.MSBuildUtils.Unescape (escapedString);
87                 }
88
89                 public static ProjectCollection GlobalProjectCollection {
90                         get { return global_project_collection; }
91                 }
92
93                 // semantic model part
94
95                 public ProjectCollection ()
96                         : this (null)
97                 {
98                 }
99
100                 public ProjectCollection (IDictionary<string, string> globalProperties)
101                 : this (globalProperties, null, ToolsetDefinitionLocations.Registry | ToolsetDefinitionLocations.ConfigurationFile)
102                 {
103                 }
104
105                 public ProjectCollection (ToolsetDefinitionLocations toolsetDefinitionLocations)
106                 : this (null, null, toolsetDefinitionLocations)
107                 {
108                 }
109
110                 public ProjectCollection (IDictionary<string, string> globalProperties, IEnumerable<ILogger> loggers,
111                                 ToolsetDefinitionLocations toolsetDefinitionLocations)
112                         : this (globalProperties, loggers, null, toolsetDefinitionLocations, 1, false)
113                 {
114                 }
115
116                 public ProjectCollection (IDictionary<string, string> globalProperties,
117                                 IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers,
118                                 ToolsetDefinitionLocations toolsetDefinitionLocations,
119                                 int maxNodeCount, bool onlyLogCriticalEvents)
120                 {
121                         global_properties = globalProperties ?? new Dictionary<string, string> ();
122                         this.loggers = loggers != null ? loggers.ToList () : new List<ILogger> ();
123                         toolset_locations = toolsetDefinitionLocations;
124                         MaxNodeCount = maxNodeCount;
125                         OnlyLogCriticalEvents = onlyLogCriticalEvents;
126
127                         LoadDefaultToolsets ();
128                 }
129                 
130                 [MonoTODO ("not fired yet")]
131                 public event ProjectAddedEventHandler ProjectAdded;
132                 [MonoTODO ("not fired yet")]
133                 public event EventHandler<ProjectChangedEventArgs> ProjectChanged;
134                 [MonoTODO ("not fired yet")]
135                 public event EventHandler<ProjectCollectionChangedEventArgs> ProjectCollectionChanged;
136                 [MonoTODO ("not fired yet")]
137                 public event EventHandler<ProjectXmlChangedEventArgs> ProjectXmlChanged;
138
139                 public void AddProject (Project project)
140                 {
141                         this.loaded_projects.Add (project);
142                         if (ProjectAdded != null)
143                                 ProjectAdded (this, new ProjectAddedToProjectCollectionEventArgs (project.Xml));
144                 }
145
146                 public int Count {
147                         get { return loaded_projects.Count; }
148                 }
149
150                 string default_tools_version;
151                 public string DefaultToolsVersion {
152                         get { return default_tools_version; }
153                         set {
154                                 if (GetToolset (value) == null)
155                                         throw new InvalidOperationException (string.Format ("Toolset '{0}' does not exist", value));
156                                 default_tools_version = value;
157                         }
158                 }
159
160                 public void Dispose ()
161                 {
162                         Dispose (true);
163                         GC.SuppressFinalize (this);
164                 }
165
166                 protected virtual void Dispose (bool disposing)
167                 {
168                         if (disposing) {
169                         }
170                 }
171
172                 public ICollection<Project> GetLoadedProjects (string fullPath)
173                 {
174                         return LoadedProjects.Where (p => p.FullPath != null && Path.GetFullPath (p.FullPath) == Path.GetFullPath (fullPath)).ToList ();
175                 }
176
177                 readonly IDictionary<string, string> global_properties;
178
179                 public IDictionary<string, string> GlobalProperties {
180                         get { return global_properties; }
181                 }
182
183                 readonly List<Project> loaded_projects = new List<Project> ();
184                 
185                 public Project LoadProject (string fileName)
186                 {
187                         return LoadProject (fileName, DefaultToolsVersion);
188                 }
189                 
190                 public Project LoadProject (string fileName, string toolsVersion)
191                 {
192                         return LoadProject (fileName, null, toolsVersion);
193                 }
194                 
195                 public Project LoadProject (string fileName, IDictionary<string,string> globalProperties, string toolsVersion)
196                 {
197                         var ret = new Project (fileName, globalProperties, toolsVersion);
198                         loaded_projects.Add (ret);
199                         return ret;
200                 }
201                 
202                 // These methods somehow don't add the project to ProjectCollection...
203                 public Project LoadProject (XmlReader xmlReader)
204                 {
205                         return LoadProject (xmlReader, DefaultToolsVersion);
206                 }
207                 
208                 public Project LoadProject (XmlReader xmlReader, string toolsVersion)
209                 {
210                         return LoadProject (xmlReader, null, toolsVersion);
211                 }
212                 
213                 public Project LoadProject (XmlReader xmlReader, IDictionary<string,string> globalProperties, string toolsVersion)
214                 {
215                         return new Project (xmlReader, globalProperties, toolsVersion);
216                 }
217                 
218                 public ICollection<Project> LoadedProjects {
219                         get { return loaded_projects; }
220                 }
221
222                 readonly List<ILogger> loggers = new List<ILogger> ();
223                 
224                 public ICollection<ILogger> Loggers {
225                         get { return loggers; }
226                 }
227
228                 [MonoTODO]
229                 public bool OnlyLogCriticalEvents { get; set; }
230
231                 [MonoTODO]
232                 public bool SkipEvaluation { get; set; }
233
234                 readonly ToolsetDefinitionLocations toolset_locations;
235                 public ToolsetDefinitionLocations ToolsetLocations {
236                         get { return toolset_locations; }
237                 }
238
239                 readonly List<Toolset> toolsets = new List<Toolset> ();
240                 // so what should we do without ToolLocationHelper in Microsoft.Build.Utilities.dll? There is no reference to it in this dll.
241                 public ICollection<Toolset> Toolsets {
242                         // For ConfigurationFile and None, they cannot be added externally.
243                         get { return (ToolsetLocations & ToolsetDefinitionLocations.Registry) != 0 ? toolsets : toolsets.ToList (); }
244                 }
245                 
246                 public Toolset GetToolset (string toolsVersion)
247                 {
248                         return Toolsets.FirstOrDefault (t => t.ToolsVersion == toolsVersion);
249                 }
250
251                 //FIXME: should also support config file, depending on ToolsetLocations
252                 void LoadDefaultToolsets ()
253                 {
254 #if NET_4_0
255                         AddToolset (new Toolset ("4.0",
256                                 ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version40), this, null));
257 #endif
258 #if XBUILD_12
259                         AddToolset (new Toolset ("12.0", ToolLocationHelper.GetPathToBuildTools ("12.0"), this, null));
260 #endif
261 #if XBUILD_14
262                         AddToolset (new Toolset ("14.0", ToolLocationHelper.GetPathToBuildTools ("14.0"), this, null));
263 #endif
264
265                         // We don't support these anymore
266                         AddToolset (new Toolset ("2.0",
267                                 ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20), this, null));
268                         AddToolset (new Toolset ("3.0",
269                                 ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version30), this, null));
270                         AddToolset (new Toolset ("3.5",
271                                 ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version35), this, null));
272
273                         default_tools_version = toolsets [0].ToolsVersion;
274                 }
275                 
276                 [MonoTODO ("not verified at all")]
277                 public void AddToolset (Toolset toolset)
278                 {
279                         toolsets.Add (toolset);
280                 }
281                 
282                 [MonoTODO ("not verified at all")]
283                 public void RemoveAllToolsets ()
284                 {
285                         toolsets.Clear ();
286                 }
287                 
288                 public void RegisterLogger (ILogger logger)
289                 {
290                         loggers.Add (logger);
291                 }
292                 
293                 public void RegisterLoggers (IEnumerable<ILogger> loggers)
294                 {
295                         foreach (var logger in loggers)
296                                 this.loggers.Add (logger);
297                 }
298
299                 public void UnloadAllProjects ()
300                 {
301                         throw new NotImplementedException ();
302                 }
303
304                 [MonoTODO ("Not verified at all")]
305                 public void UnloadProject (Project project)
306                 {
307                         this.loaded_projects.Remove (project);
308                 }
309
310                 [MonoTODO ("Not verified at all")]
311                 public void UnloadProject (ProjectRootElement projectRootElement)
312                 {
313                         foreach (var proj in loaded_projects.Where (p => p.Xml == projectRootElement).ToArray ())
314                                 UnloadProject (proj);
315                 }
316
317                 public static Version Version {
318                         get { throw new NotImplementedException (); }
319                 }
320
321                 // Execution part
322
323                 [MonoTODO]
324                 public bool DisableMarkDirty { get; set; }
325
326                 [MonoTODO]
327                 public HostServices HostServices { get; set; }
328
329                 [MonoTODO]
330                 public bool IsBuildEnabled { get; set; }
331                 
332                 internal string BuildStartupDirectory { get; set; }
333                 
334                 internal int MaxNodeCount { get; private set; }
335                 
336                 Stack<string> ongoing_imports = new Stack<string> ();
337                 
338                 internal Stack<string> OngoingImports {
339                         get { return ongoing_imports; }
340                 }
341                 
342                 // common part
343                 internal static IEnumerable<EnvironmentProjectProperty> GetWellKnownProperties (Project project)
344                 {
345                         Func<string,string,EnvironmentProjectProperty> create = (name, value) => new EnvironmentProjectProperty (project, name, value, true);
346                         return GetWellKnownProperties (create);
347                 }
348                 
349                 internal static IEnumerable<ProjectPropertyInstance> GetWellKnownProperties (ProjectInstance project)
350                 {
351                         Func<string,string,ProjectPropertyInstance> create = (name, value) => new ProjectPropertyInstance (name, true, value);
352                         return GetWellKnownProperties (create);
353                 }
354                 
355                 static IEnumerable<T> GetWellKnownProperties<T> (Func<string,string,T> create)
356                 {
357                         yield return create ("OS", OS);
358                         var ext = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath") ?? DefaultExtensionsPath;
359                         yield return create ("MSBuildExtensionsPath", ext);
360                         var ext32 = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath32") ?? ext;
361                         yield return create ("MSBuildExtensionsPath32", ext32);
362                         var ext64 = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath64") ?? ext;
363                         yield return create ("MSBuildExtensionsPath64", ext64);
364                 }
365
366                 static string OS {
367                         get {
368                                 PlatformID pid = Environment.OSVersion.Platform;
369                                 switch ((int) pid) {
370                                 case 128:
371                                 case 4:
372                                         return "Unix";
373                                 case 6:
374                                         return "OSX";
375                                 default:
376                                         return "Windows_NT";
377                                 }
378                         }
379                 }
380
381                 #region Extension Paths resolution
382
383                 static string extensions_path;
384                 internal static string DefaultExtensionsPath {
385                         get {
386                                 if (extensions_path == null) {
387                                         // NOTE: code from mcs/tools/gacutil/driver.cs
388                                         PropertyInfo gac = typeof (System.Environment).GetProperty (
389                                                         "GacPath", BindingFlags.Static | BindingFlags.NonPublic);
390
391                                         if (gac != null) {
392                                                 MethodInfo get_gac = gac.GetGetMethod (true);
393                                                 string gac_path = (string) get_gac.Invoke (null, null);
394                                                 extensions_path = Path.GetFullPath (Path.Combine (
395                                                                         gac_path, Path.Combine ("..", "xbuild")));
396                                         }
397                                 }
398                                 return extensions_path;
399                         }
400                 }
401
402                 static string DotConfigExtensionsPath = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
403                         Path.Combine ("xbuild", "tasks"));
404                 const string MacOSXExternalXBuildDir = "/Library/Frameworks/Mono.framework/External/xbuild";
405                 static string PathSeparatorAsString = Path.PathSeparator.ToString ();
406
407                 // Gives a list of extensions paths to try for $(MSBuildExtensionsPath),
408                 // *in-order*
409                 internal static IEnumerable<string> GetApplicableExtensionsPaths (Action<string> logMessage)
410                 {
411                         string envvar = String.Join (PathSeparatorAsString, new string [] {
412                                 // For mac osx, look in the 'External' dir on macosx,
413                                 // see bug #663180
414                                 MSBuildUtils.RunningOnMac ? MacOSXExternalXBuildDir : String.Empty,
415                                 DotConfigExtensionsPath,
416                                 DefaultExtensionsPath});
417
418                         var pathsTable = new Dictionary<string, string> ();
419                         foreach (string extn_path in envvar.Split (new char [] {Path.PathSeparator}, StringSplitOptions.RemoveEmptyEntries)) {
420                                 if (pathsTable.ContainsKey (extn_path))
421                                         continue;
422
423                                 if (!Directory.Exists (extn_path)) {
424                                         logMessage (string.Format ("Extension path '{0}' not found, ignoring.", extn_path));
425                                         continue;
426                                 }
427
428                                 pathsTable [extn_path] = extn_path;
429                                 yield return extn_path;
430                         }
431                 }
432
433                 internal static string FindFileInSeveralExtensionsPath (ref string extensionsPathOverride, Func<string,string> expandString, string file, Action<string> logMessage)
434                 {
435                         string ret = null;
436                         string ex = extensionsPathOverride;
437                         Func<bool> action = () => {
438                                 string path = WindowsCompatibilityExtensions.FindMatchingPath (expandString (file));
439                                 if (File.Exists (path))
440                                         ret = path;
441                                 else
442                                         return false;
443                                 return true;
444                         };
445
446                         try {
447                                 if (!action ()) {
448                                         foreach (var s in ProjectCollection.GetApplicableExtensionsPaths (logMessage)) {
449                                                 extensionsPathOverride = s;
450                                                 ex = s;
451                                                 if (action ())
452                                                         break;
453                                         }
454                                 }
455                         } finally {
456                                 extensionsPathOverride = null;
457                         }
458
459                         return ret ?? WindowsCompatibilityExtensions.FindMatchingPath (expandString (file));
460                 }
461
462                 #endregion
463
464                 internal IEnumerable<ReservedProjectProperty> GetReservedProperties (Toolset toolset, Project project)
465                 {
466                         Func<string,Func<string>,ReservedProjectProperty> create = (name, value) => new ReservedProjectProperty (project, name, value);
467                         return GetReservedProperties<ReservedProjectProperty> (toolset, project.Xml, create, () => project.FullPath);
468                 }
469                 
470                 internal IEnumerable<ProjectPropertyInstance> GetReservedProperties (Toolset toolset, ProjectInstance project, ProjectRootElement xml)
471                 {
472                         Func<string,Func<string>,ProjectPropertyInstance> create = (name, value) => new ProjectPropertyInstance (name, true, null, value);
473                         return GetReservedProperties<ProjectPropertyInstance> (toolset, xml, create, () => project.FullPath);
474                 }
475                 
476                 // seealso http://msdn.microsoft.com/en-us/library/ms164309.aspx
477                 IEnumerable<T> GetReservedProperties<T> (Toolset toolset, ProjectRootElement project, Func<string,Func<string>,T> create, Func<string> projectFullPath)
478                 {
479                         yield return create ("MSBuildBinPath", () => toolset.ToolsPath);
480                         // FIXME: add MSBuildLastTaskResult
481                         // FIXME: add MSBuildNodeCount
482                         // FIXME: add MSBuildProgramFiles32
483                         yield return create ("MSBuildProjectDefaultTargets", () => project.DefaultTargets);
484                         yield return create ("MSBuildProjectDirectory", () => project.DirectoryPath + Path.DirectorySeparatorChar);
485                         yield return create ("MSBuildProjectDirectoryNoRoot", () => project.DirectoryPath.Substring (Path.GetPathRoot (project.DirectoryPath).Length));
486                         yield return create ("MSBuildProjectExtension", () => Path.GetExtension (project.FullPath));
487                         yield return create ("MSBuildProjectFile", () => Path.GetFileName (project.FullPath));
488                         yield return create ("MSBuildProjectFullPath", () => project.FullPath);
489                         yield return create ("MSBuildProjectName", () => Path.GetFileNameWithoutExtension (project.FullPath));
490                         yield return create ("MSBuildStartupDirectory", () => BuildStartupDirectory);
491                         yield return create ("MSBuildThisFile", () => Path.GetFileName (GetEvaluationTimeThisFile (projectFullPath)));
492                         yield return create ("MSBuildThisFileFullPath", () => GetEvaluationTimeThisFile (projectFullPath));
493                         yield return create ("MSBuildThisFileName", () => Path.GetFileNameWithoutExtension (GetEvaluationTimeThisFile (projectFullPath)));
494                         yield return create ("MSBuildThisFileExtension", () => Path.GetExtension (GetEvaluationTimeThisFile (projectFullPath)));
495
496                         yield return create ("MSBuildThisFileDirectory", () => Path.GetDirectoryName (GetEvaluationTimeThisFileDirectory (projectFullPath)));
497                         yield return create ("MSBuildThisFileDirectoryNoRoot", () => {
498                                 string dir = GetEvaluationTimeThisFileDirectory (projectFullPath) + Path.DirectorySeparatorChar;
499                                 return dir.Substring (Path.GetPathRoot (dir).Length);
500                                 });
501                         yield return create ("MSBuildToolsPath", () => toolset.ToolsPath);
502                         yield return create ("MSBuildToolsVersion", () => toolset.ToolsVersion);
503
504                         // This is an implementation specific special property for this Microsoft.Build.dll to differentiate
505                         // the build from Microsoft.Build.Engine.dll. It is significantly used in some *.targets file we share
506                         // between old and new build engine.
507                         yield return create ("MonoUseMicrosoftBuildDll", () => "True");
508                 }
509                 
510                 // These are required for reserved property, represents dynamically changing property values.
511                 // This should resolve to either the project file path or that of the imported file.
512                 internal string GetEvaluationTimeThisFileDirectory (Func<string> nonImportingTimeFullPath)
513                 {
514                         var file = GetEvaluationTimeThisFile (nonImportingTimeFullPath);
515                         var dir = Path.IsPathRooted (file) ? Path.GetDirectoryName (file) : Directory.GetCurrentDirectory ();
516                         return dir + Path.DirectorySeparatorChar;
517                 }
518
519                 internal string GetEvaluationTimeThisFile (Func<string> nonImportingTimeFullPath)
520                 {
521                         return OngoingImports.Count > 0 ? OngoingImports.Peek () : (nonImportingTimeFullPath () ?? string.Empty);
522                 }
523                 
524                 static readonly char [] item_target_sep = {';'};
525                 
526                 internal static IEnumerable<T> GetAllItems<T> (Func<string,string> expandString, string include, string exclude, Func<string,T> creator, Func<string,ITaskItem> taskItemCreator, string directory, Action<T,string> assignRecurse, Func<ITaskItem,bool> isDuplicate)
527                 {
528                         var includes = expandString (include).Trim ().Split (item_target_sep, StringSplitOptions.RemoveEmptyEntries);
529                         var excludes = expandString (exclude).Trim ().Split (item_target_sep, StringSplitOptions.RemoveEmptyEntries);
530                         
531                         if (includes.Length == 0)
532                                 yield break;
533                         if (includes.Length == 1 && includes [0].IndexOf ('*') < 0 && excludes.Length == 0) {
534                                 // for most case - shortcut.
535                                 var item = creator (includes [0]);
536                                 yield return item;
537                         } else {
538                                 var ds = new Microsoft.Build.BuildEngine.DirectoryScanner () {
539                                         BaseDirectory = new DirectoryInfo (directory),
540                                         Includes = includes.Where (s => !string.IsNullOrWhiteSpace (s)).Select (i => taskItemCreator (i)).ToArray (),
541                                         Excludes = excludes.Where (s => !string.IsNullOrWhiteSpace (s)).Select (e => taskItemCreator (e)).ToArray (),
542                                 };
543                                 ds.Scan ();
544                                 foreach (var taskItem in ds.MatchedItems) {
545                                         if (isDuplicate (taskItem))
546                                                 continue; // skip duplicate
547                                         var item = creator (taskItem.ItemSpec);
548                                         string recurse = taskItem.GetMetadata ("RecursiveDir");
549                                         assignRecurse (item, recurse);
550                                         yield return item;
551                                 }
552                         }
553                 }
554                 
555                 static readonly char [] path_sep = {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar};
556                 
557                 internal static string GetWellKnownMetadata (string name, string file, Func<string,string> getFullPath, string recursiveDir)
558                 {
559                         switch (name.ToLower (CultureInfo.InvariantCulture)) {
560                         case "fullpath":
561                                 return getFullPath (file);
562                         case "rootdir":
563                                 return Path.GetPathRoot (getFullPath (file));
564                         case "filename":
565                                 return Path.GetFileNameWithoutExtension (file);
566                         case "extension":
567                                 return Path.GetExtension (file);
568                         case "relativedir":
569                                         var idx = file.LastIndexOfAny (path_sep);
570                                         return idx < 0 ? string.Empty : file.Substring (0, idx + 1);
571                         case "directory":
572                                         var fp = getFullPath (file);
573                                         return Path.GetDirectoryName (fp).Substring (Path.GetPathRoot (fp).Length);
574                         case "recursivedir":
575                                 return recursiveDir;
576                         case "identity":
577                                 return file;
578                         case "modifiedtime":
579                                 return new FileInfo (getFullPath (file)).LastWriteTime.ToString ("yyyy-MM-dd HH:mm:ss.fffffff");
580                         case "createdtime":
581                                 return new FileInfo (getFullPath (file)).CreationTime.ToString ("yyyy-MM-dd HH:mm:ss.fffffff");
582                         case "accessedtime":
583                                 return new FileInfo (getFullPath (file)).LastAccessTime.ToString ("yyyy-MM-dd HH:mm:ss.fffffff");
584                         }
585                         return null;
586                 }
587         }
588 }