public void Start() {
			tracker = CreateProgressMonitor ();
			tracker.BeginTask(GetDescription(), 1);

			// Sync invoke background worker which will end up doing async invoke on the internal run.
			BackgroundWorker ();
		}
		public ProgressMonitorWrapperJob (IAnalysisJob wrappedJob, string message)
		{
			this.wrappedJob = wrappedJob;
			monitor = IdeApp.Workbench.ProgressMonitors.GetStatusProgressMonitor (message, null, false);
			var work = wrappedJob.GetFiles ().Sum (f => wrappedJob.GetIssueProviders (f).Count ());
			
			monitor.BeginTask (message, work);
		}
		public Task WriteFile (string file, object obj, bool saveProjects, ProgressMonitor monitor)
		{
			return Task.Run (delegate {
				Solution sol = (Solution)obj;

				try {
					monitor.BeginTask (GettextCatalog.GetString ("Saving solution: {0}", file), 1);
					WriteFileInternal (file, file, sol, saveProjects, monitor);
				} catch (Exception ex) {
					monitor.ReportError (GettextCatalog.GetString ("Could not save solution: {0}", file), ex);
					LoggingService.LogError (GettextCatalog.GetString ("Could not save solution: {0}", file), ex);
					throw;
				} finally {
					monitor.EndTask ();
				}
			});
		}
		WorkspaceItem ReadWorkspaceItemFile (FilePath fileName, ProgressMonitor monitor)
		{
			XmlTextReader reader = new XmlTextReader (new StreamReader (fileName));
			try {
				monitor.BeginTask (string.Format (GettextCatalog.GetString ("Loading workspace item: {0}"), fileName), 1);
				reader.MoveToContent ();
				XmlDataSerializer ser = new XmlDataSerializer (MD1ProjectService.DataContext);
				ser.SerializationContext.BaseFile = fileName;
				ser.SerializationContext.ProgressMonitor = monitor;
				WorkspaceItem entry = (WorkspaceItem)ser.Deserialize (reader, typeof(WorkspaceItem));
				entry.FileName = fileName;
				return entry;
			} catch (Exception ex) {
				monitor.ReportError (string.Format (GettextCatalog.GetString ("Could not load solution item: {0}"), fileName), ex);
				throw;
			} finally {
				monitor.EndTask ();
				reader.Close ();
			}
		}
		void InstallEntry (ProgressMonitor monitor, DeployContext ctx, SolutionFolderItem entry, ConfigurationSelector configuration)
		{
			foreach (DeployFile df in DeployService.GetDeployFiles (ctx, new SolutionFolderItem[] { entry }, configuration)) {
				string targetPath = df.ResolvedTargetFile;
				if (targetPath == null) {
					monitor.ReportWarning ("Could not copy file '" + df.RelativeTargetPath + "': Unknown target directory.");
					continue;
				}
				
				CopyFile (monitor, df.SourcePath, df.ResolvedTargetFile, df.FileAttributes);
			}
			
			SolutionFolder c = entry as SolutionFolder;
			if (c != null) {
				monitor.BeginTask ("Installing solution '" + c.Name + "'", c.Items.Count);
				foreach (SolutionFolderItem ce in c.Items) {
					InstallEntry (monitor, ctx, ce, configuration);
					monitor.Step (1);
				}
				monitor.EndTask ();
			}
		}
		internal bool Build (ProgressMonitor monitor)
		{
			monitor.BeginTask ("Package: " + Description, 1);
			DeployContext ctx = null;
			try {
				ctx = CreateDeployContext ();
				if (ctx != null)
					ctx.FileFilter = this;
				if (!OnBuild (monitor, ctx)) {
					return false;
				}
			} catch (Exception ex) {
				monitor.ReportError ("Package creation failed", ex);
				LoggingService.LogError ("Package creation failed", ex);
				return false;
			} finally {
				monitor.EndTask ();
				if (ctx != null)
					ctx.Dispose ();
			}
			return true;
		}
		public Makefile Deploy (AutotoolsContext ctx, SolutionFolderItem entry, ProgressMonitor monitor)
		{
			generateAutotools = ctx.MakefileType == MakefileType.AutotoolsMakefile;
			
			monitor.BeginTask ( GettextCatalog.GetString (
						"Creating {0} for Solution {1}",
						generateAutotools ? "Makefile.am" : "Makefile", entry.Name), 1 );

			Makefile solutionMakefile = new Makefile ();
			StringBuilder solutionTop = new StringBuilder ();

			try
			{
				SolutionFolder solutionFolder = (SolutionFolder) entry;
				string targetDirectory = solutionFolder.BaseDirectory;

				StringBuilder subdirs = new StringBuilder();
				subdirs.Append ("#Warning: This is an automatically generated file, do not edit!\n");

				if (!generateAutotools) {
					solutionTop.AppendFormat ("top_srcdir={0}\n", FileService.AbsoluteToRelativePath (
							entry.BaseDirectory, ctx.TargetSolution.BaseDirectory));
					solutionTop.Append ("include $(top_srcdir)/config.make\n");
					solutionTop.Append ("include $(top_srcdir)/Makefile.include\n");
					solutionTop.Append ("include $(top_srcdir)/rules.make\n\n");
					solutionTop.Append ("#include $(top_srcdir)/custom-hooks.make\n\n");
				}

				ArrayList children = new ArrayList ();
				foreach ( SolutionConfiguration config in solutionFolder.ParentSolution.Configurations )
				{
					if ( !ctx.IsSupportedConfiguration ( config.Id ) ) continue;
					
					if (generateAutotools)
						subdirs.AppendFormat ( "if {0}\n", "ENABLE_" + ctx.EscapeAndUpperConfigName (config.Id));
					else
						subdirs.AppendFormat ( "ifeq ($(CONFIG),{0})\n", ctx.EscapeAndUpperConfigName (config.Id));

					subdirs.Append (" SUBDIRS = ");
					
					foreach (SolutionFolderItem ce in CalculateSubDirOrder (ctx, solutionFolder, config))
					{
						string baseDirectory;
						if (!(ce is SolutionItem) && !(ce is SolutionFolder))
							continue;
						
						// Ignore projects which can't be deployed
						IMakefileHandler handler = AutotoolsContext.GetMakefileHandler (ce, ctx.MakefileType);
						if (handler == null)
							continue;
							
						baseDirectory = ce.BaseDirectory;
						
						if (solutionFolder.BaseDirectory == baseDirectory) {
							subdirs.Append (" . ");
						} else {
							if (!baseDirectory.StartsWith (solutionFolder.BaseDirectory) )
								throw new Exception ( GettextCatalog.GetString (
									"Child projects must be in sub-directories of their parent") );
							
							// add the subdirectory to the list
							string path = FileService.AbsoluteToRelativePath (targetDirectory, baseDirectory);
							if (path.StartsWith ("." + Path.DirectorySeparatorChar) )
								path = path.Substring (2);
							
							AutotoolsContext.CheckSpaces (path);
							subdirs.Append (" ");
							subdirs.Append ( AutotoolsContext.EscapeStringForAutomake (path) );
						}

						if (!children.Contains (ce))
							children.Add ( ce );
					}
					subdirs.Append ( "\nendif\n" );
				}
				solutionTop.Append ( subdirs.ToString () );

				string includedProject = null;

				// deploy recursively
				foreach (SolutionFolderItem ce in children)
				{
					IMakefileHandler handler = AutotoolsContext.GetMakefileHandler ( ce, ctx.MakefileType );
					Makefile makefile;
					string outpath;
					if ( handler != null && handler.CanDeploy ( ce, ctx.MakefileType ) )
					{
						ctx.RegisterBuiltProject (ce);
						makefile = handler.Deploy ( ctx, ce, monitor );

						if (targetDirectory == ce.BaseDirectory)
						{
							if (includedProject != null)
								throw new Exception ( GettextCatalog.GetString (
									"More than 1 project in the same directory as the top-level solution is not supported."));

							// project is in the solution directory
							string projectMakefileName = ce.Name + ".make";
							includedProject = String.Format ("include {0}", projectMakefileName);
							outpath = Path.Combine (targetDirectory, projectMakefileName);
							ctx.AddGeneratedFile (outpath);

							if (!generateAutotools)
								solutionMakefile.SetVariable ("EXTRA_DIST", projectMakefileName);
						} else {
							makefile.AppendToVariable ("EXTRA_DIST", generateAutotools ? String.Empty : "Makefile");
							outpath = Path.Combine (ce.BaseDirectory, "Makefile");
							if (generateAutotools) {
								ctx.AddAutoconfFile (outpath);
								outpath = outpath + ".am";
							} else {
								makefile.Append ("install: install-local\nuninstall: uninstall-local\nclean: clean-local\n");
								if (ce is SolutionFolder)
									//non TargetCombine
									makefile.Append ("dist-local: dist-local-recursive\n");
								else
									makefile.Append ("include $(top_srcdir)/rules.make\n");
							}
							ctx.AddGeneratedFile (outpath);
						}

						StreamWriter writer = new StreamWriter (outpath);
						makefile.Write ( writer );
						writer.Close ();
					}
					else {
						monitor.Log .WriteLine("Project '{0}' skipped.", ce.Name); 
					}
				}

				if (includedProject != null) {
					solutionTop.Append (GettextCatalog.GetString ("\n# Include project specific makefile\n"));
					solutionTop.Append (includedProject);
				}

				if (generateAutotools) {
					solutionMakefile.Append (solutionTop.ToString ());
				} else {
					TemplateEngine templateEngine = new TemplateEngine ();
					templateEngine.Variables ["MAKEFILE_SOLUTION_TOP"] = solutionTop.ToString ();

					Stream stream = ctx.GetTemplateStream ("Makefile.solution.template");
					StreamReader reader = new StreamReader (stream);
					StringWriter sw = new StringWriter ();

					templateEngine.Process (reader, sw);
					reader.Close ();

					solutionMakefile.Append (sw.ToString ());

					if (solutionFolder.IsRoot) {
						// Emit dist and distcheck targets only for TargetCombine
						reader = new StreamReader (Path.Combine (ctx.TemplateDir, "make-dist.targets"));
						solutionMakefile.Append (reader.ReadToEnd ());
						reader.Close ();
					}
				}

				monitor.Step (1);
			}
			finally
			{
				monitor.EndTask ();
			}
			return solutionMakefile;
		}
		//Reader
		public async Task<object> ReadFile (string fileName, ProgressMonitor monitor)
		{
			if (fileName == null || monitor == null)
				return null;

			var sol = new Solution (true);
			sol.FileName = fileName;
			sol.FileFormat = format;

			try {
				monitor.BeginTask (string.Format (GettextCatalog.GetString ("Loading solution: {0}"), fileName), 1);
				monitor.BeginStep ();
				await sol.OnBeginLoad ();
				var projectLoadMonitor = monitor as ProjectLoadProgressMonitor;
				if (projectLoadMonitor != null)
					projectLoadMonitor.CurrentSolution = sol;
				await Task.Factory.StartNew (() => {
					sol.ReadSolution (monitor);
				});
			} catch (Exception ex) {
				monitor.ReportError (GettextCatalog.GetString ("Could not load solution: {0}", fileName), ex);
				sol.OnEndLoad ().Wait ();
				sol.NotifyItemReady ();
				monitor.EndTask ();
				throw;
			}
			await sol.OnEndLoad ();
			sol.NotifyItemReady ();
			monitor.EndTask ();
			return sol;
		}
		internal void WriteFileInternal (SlnFile sln, Solution solution, ProgressMonitor monitor)
		{
			SolutionFolder c = solution.RootFolder;

			// Delete data for projects that have been removed from the solution

			var currentProjects = new HashSet<string> (solution.GetAllItems<SolutionFolderItem> ().Select (it => it.ItemId));
			var removedProjects = new HashSet<string> ();
			if (solution.LoadedProjects != null)
				removedProjects.UnionWith (solution.LoadedProjects.Except (currentProjects));
			var unknownProjects = new HashSet<string> (sln.Projects.Select (p => p.Id).Except (removedProjects).Except (currentProjects));

			foreach (var p in removedProjects) {
				var ps = sln.Projects.GetProject (p);
				if (ps != null)
					sln.Projects.Remove (ps);
				var pc = sln.ProjectConfigurationsSection.GetPropertySet (p, true);
				if (pc != null)
					sln.ProjectConfigurationsSection.Remove (pc);
			}
			var secNested = sln.Sections.GetSection ("NestedProjects");
			if (secNested != null) {
				foreach (var np in secNested.Properties.ToArray ()) {
					if (removedProjects.Contains (np.Key) || removedProjects.Contains (np.Value))
						secNested.Properties.Remove (np.Key);
				}
			}
			solution.LoadedProjects = currentProjects;

			//Write the projects
			using (monitor.BeginTask (GettextCatalog.GetString ("Saving projects"), 1)) {
				monitor.BeginStep ();
				WriteProjects (c, sln, monitor, unknownProjects);
			}

			//FIXME: SolutionConfigurations?

			var pset = sln.SolutionConfigurationsSection;
			foreach (SolutionConfiguration config in solution.Configurations) {
				var cid = ToSlnConfigurationId (config);
				pset.SetValue (cid, cid);
			}

			WriteProjectConfigurations (solution, sln);

			//Write Nested Projects
			ICollection<SolutionFolder> folders = solution.RootFolder.GetAllItems<SolutionFolder> ().ToList ();
			if (folders.Count > 1) {
				// If folders ==1, that's the root folder
				var sec = sln.Sections.GetOrCreateSection ("NestedProjects", SlnSectionType.PreProcess);
				foreach (SolutionFolder folder in folders) {
					if (folder.IsRoot)
						continue;
					WriteNestedProjects (folder, solution.RootFolder, sec);
				}
				// Remove items which don't have a parent folder
				var toRemove = solution.GetAllItems<SolutionFolderItem> ().Where (it => it.ParentFolder == solution.RootFolder);
				foreach (var it in toRemove)
					sec.Properties.Remove (it.ItemId);
			}

			// Write custom properties for configurations
			foreach (SolutionConfiguration conf in solution.Configurations) {
				string secId = "MonoDevelopProperties." + conf.Id;
				var sec = sln.Sections.GetOrCreateSection (secId, SlnSectionType.PreProcess);
				solution.WriteConfigurationData (monitor, sec.Properties, conf);
				if (sec.IsEmpty)
					sln.Sections.Remove (sec);
			}
		}
		static async Task Update (ProgressMonitor monitor, IEnumerator<ProjectFile> fileEnumerator, bool force, int succeeded, int warnings, int errors)
		{
			ProjectFile file = fileEnumerator.Current;
			ISingleFileCustomTool tool;
			ProjectFile genFile;

			bool shouldRun;
			while (!(shouldRun = ShouldRunGenerator (file, force, out tool, out genFile)) && fileEnumerator.MoveNext ())
				continue;

			//no files which can be generated in remaining elements of the collection, nothing to do
			if (!shouldRun) {
				WriteSummaryResults (monitor, succeeded, warnings, errors);
				return;
			}

			TaskService.Errors.ClearByOwner (file);

			var result = new SingleFileCustomToolResult ();
			monitor.BeginTask (GettextCatalog.GetString ("Running generator '{0}' on file '{1}'...", file.Generator, file.Name), 1);

			try {
				await tool.Generate (monitor, file, result);
				if (!monitor.HasErrors && !monitor.HasWarnings) {
					monitor.Log.WriteLine (GettextCatalog.GetString ("File '{0}' was generated successfully.", result.GeneratedFilePath));
					succeeded++;
				} else if (!monitor.HasErrors) {
					monitor.Log.WriteLine (GettextCatalog.GetString ("File '{0}' was generated with warnings.", result.GeneratedFilePath));
					warnings++;
				} else {
					monitor.Log.WriteLine (GettextCatalog.GetString ("Errors in file '{0}' generation.", result.GeneratedFilePath));
					errors++;
				}

				//check that we can process further. If UpdateCompleted returns `true` this means no errors or non-fatal errors occured
				if (UpdateCompleted (monitor, file, genFile, result, true) && fileEnumerator.MoveNext ())
					await Update (monitor, fileEnumerator, force, succeeded, warnings, errors);
				else
					WriteSummaryResults (monitor, succeeded, warnings, errors);
			} catch (Exception ex) {
				result.UnhandledException = ex;
				UpdateCompleted (monitor, file, genFile, result, true);
			}
		}
		public StashApplyStatus PopStash (ProgressMonitor monitor, int stashIndex)
		{
			if (monitor != null)
				monitor.BeginTask ("Applying stash", 1);

			var stash = RootRepository.Stashes [stashIndex];
			int progress = 0;
			StashApplyStatus res = RootRepository.Stashes.Pop (stashIndex, new StashApplyOptions {
				CheckoutOptions = new CheckoutOptions {
					OnCheckoutProgress = (path, completedSteps, totalSteps) => OnCheckoutProgress (completedSteps, totalSteps, monitor, ref progress),
					OnCheckoutNotify = RefreshFile,
					CheckoutNotifyFlags = refreshFlags,
				},
			});
			NotifyFilesChangedForStash (stash);
			if (monitor != null)
				monitor.EndTask ();

			return res;
		}
		static void OnCheckoutProgress (int completedSteps, int totalSteps, ProgressMonitor monitor, ref int progress)
		{
			if (progress == 0 && completedSteps == 0) {
				monitor.BeginTask ("Checking out files", totalSteps);
				throttleWatch.Restart ();
			}

			int steps = completedSteps - progress;
			if (throttleWatch.ElapsedMilliseconds > progressThrottle) {
				monitor.Step (steps);
				throttleWatch.Restart ();
			}
			progress = completedSteps;

			if (completedSteps >= totalSteps) {
				monitor.EndTask ();
				throttleWatch.Stop ();
			}
		}
		static bool OnTransferProgress (TransferProgress tp, ProgressMonitor monitor, ref int progress)
		{
			if (progress == 0 && tp.ReceivedObjects == 0) {
				monitor.BeginTask ("Receiving and indexing objects", 2 * tp.TotalObjects);
				throttleWatch.Restart ();
			}

			int currentProgress = tp.ReceivedObjects + tp.IndexedObjects;
			int steps = currentProgress - progress;
			if (throttleWatch.ElapsedMilliseconds > progressThrottle) {
				monitor.Step (steps);
				throttleWatch.Restart ();
			}
			progress = currentProgress;

			if (tp.IndexedObjects >= tp.TotalObjects) {
				monitor.EndTask ();
				throttleWatch.Stop ();
			}

			return !monitor.CancellationToken.IsCancellationRequested;
		}
		protected override void OnMoveDirectory (FilePath localSrcPath, FilePath localDestPath, bool force, ProgressMonitor monitor)
		{
			VersionInfo[] versionedFiles = GetDirectoryVersionInfo (localSrcPath, false, true);
			base.OnMoveDirectory (localSrcPath, localDestPath, force, monitor);
			monitor.BeginTask ("Moving files", versionedFiles.Length);
			foreach (VersionInfo vif in versionedFiles) {
				if (vif.IsDirectory)
					continue;
				FilePath newDestPath = vif.LocalPath.ToRelative (localSrcPath).ToAbsolute (localDestPath);
				Add (newDestPath, false, monitor);
				monitor.Step (1);
			}
			monitor.EndTask ();
		}
		void NotifyFileChanges (ProgressMonitor monitor, TreeChanges statusList)
		{
			// Files added to source branch not present to target branch.
			var removed = statusList.Where (c => c.Status == ChangeKind.Added).Select (c => GetRepository (c.Path).FromGitPath (c.Path)).ToList ();
			var modified = statusList.Where (c => c.Status != ChangeKind.Added).Select (c => GetRepository (c.Path).FromGitPath (c.Path)).ToList ();

			monitor.BeginTask (GettextCatalog.GetString ("Updating solution"), removed.Count + modified.Count);

			FileService.NotifyFilesChanged (modified, true);
			monitor.Step (modified.Count);

			FileService.NotifyFilesRemoved (removed);
			monitor.Step (removed.Count);

			monitor.EndTask ();
		}
		/// <summary>
		/// Validates the schema.
		/// </summary>		
		public static XmlSchema ValidateSchema (ProgressMonitor monitor, string xml, string fileName)
		{
			monitor.BeginTask (GettextCatalog.GetString ("Validating schema..."), 1);
			bool error = false;
			XmlSchema schema = null;
			try {
				StringReader stringReader = new StringReader (xml);
				XmlTextReader xmlReader = new XmlTextReader (stringReader);
				xmlReader.XmlResolver = null;
				
				ValidationEventHandler callback = delegate (object source, ValidationEventArgs args) {
					if (args.Severity == XmlSeverityType.Warning) {
						monitor.ReportWarning (args.Message);
					} else {
						monitor.ReportError (args.Message, args.Exception);
						error = true;
					}
					AddTask (fileName, args.Message, args.Exception.LinePosition, args.Exception.LineNumber,
					    (args.Severity == XmlSeverityType.Warning)? TaskSeverity.Warning : TaskSeverity.Error);
				};
				schema = XmlSchema.Read (xmlReader, callback);
				XmlSchemaSet sset = new XmlSchemaSet ();
				sset.Add (schema);
				sset.ValidationEventHandler += callback;
				sset.Compile ();
			} 
			catch (XmlSchemaException ex) {
				monitor.ReportError (ex.Message, ex);
				AddTask (fileName, ex.Message, ex.LinePosition, ex.LineNumber,TaskSeverity.Error);
				error = true;
			}
			catch (XmlException ex) {
				monitor.ReportError (ex.Message, ex);
				AddTask (fileName, ex.Message, ex.LinePosition, ex.LineNumber,TaskSeverity.Error);
				error = true;
			}
			
			if (error) {
				monitor.Log.WriteLine (GettextCatalog.GetString ("Validation failed."));
				TaskService.ShowErrors ();
			} else {
				monitor.Log.WriteLine  (GettextCatalog.GetString ("Schema is valid."));
			}
			
			monitor.EndTask ();
			return error? null: schema;
		}
		public static XslCompiledTransform ValidateStylesheet (ProgressMonitor monitor, string xml, string fileName)
		{
			monitor.BeginTask (GettextCatalog.GetString ("Validating stylesheet..."), 1);
			bool error = true;
			XslCompiledTransform xslt = null;
			
			try {
				StringReader reader = new StringReader (xml);
				XPathDocument doc = new XPathDocument (reader);
				xslt = new XslCompiledTransform ();
				xslt.Load (doc, null, new XmlUrlResolver ());
				error = false;
			} catch (XsltCompileException ex) {
				monitor.ReportError (ex.Message, ex);
				AddTask (fileName, ex.Message, ex.LinePosition, ex.LineNumber,TaskSeverity.Error);
			}
			catch (XsltException ex) {
				monitor.ReportError (ex.Message, ex);
				AddTask (fileName, ex.Message, ex.LinePosition, ex.LineNumber,TaskSeverity.Error);
			}
			catch (XmlException ex) {
				monitor.ReportError (ex.Message, ex);
				AddTask (fileName, ex.Message, ex.LinePosition, ex.LineNumber,TaskSeverity.Error);
			}
			
			if (error) {
				monitor.Log.WriteLine (GettextCatalog.GetString ("Validation failed."));
				TaskService.ShowErrors ();
			} else {
				monitor.Log.WriteLine (GettextCatalog.GetString ("Stylesheet is valid."));
			}
			return error? null: xslt;
		}
		public bool TryCreateStash (ProgressMonitor monitor, string message, out Stash stash)
		{
			Signature sig = GetSignature ();
			stash = null;
			if (sig == null)
				return false;

			if (monitor != null)
				monitor.BeginTask ("Stashing changes", 1);

			stash = RootRepository.Stashes.Add (sig, message, StashModifiers.Default | StashModifiers.IncludeUntracked);

			if (monitor != null)
				monitor.EndTask ();
			return true;
		}
		public static Task WaitForRunningTools (ProgressMonitor monitor)
		{
			TaskInfo[] operations;
			lock (runningTasks) {
				operations = runningTasks.Values.ToArray ();
			}

			if (operations.Length == 0)
				return Task.FromResult (true);

			monitor.BeginTask ("Waiting for custom tools...", operations.Length);

			List<Task> tasks = new List<Task> ();

			foreach (var t in operations) {
				tasks.Add (t.Task.ContinueWith (ta => {
					if (!monitor.CancellationToken.IsCancellationRequested)
						monitor.Step (1);
				}));
			}

			var cancelTask = new TaskCompletionSource<bool> ();
			var allDone = Task.WhenAll (tasks);

			var cancelReg = monitor.CancellationToken.Register (() => {
				cancelTask.SetResult (true);
			});

			return Task.WhenAny (allDone, cancelTask.Task).ContinueWith (t => {
				monitor.EndTask (); 
				cancelReg.Dispose ();
			});
		}
		protected override void OnUpdate (FilePath[] localPaths, bool recurse, ProgressMonitor monitor)
		{
			// TODO: Make it work differently for submodules.
			monitor.BeginTask (GettextCatalog.GetString ("Updating"), 5);

			if (RootRepository.Head.IsTracking) {
				Fetch (monitor, RootRepository.Head.Remote.Name);

				GitUpdateOptions options = GitService.StashUnstashWhenUpdating ? GitUpdateOptions.NormalUpdate : GitUpdateOptions.UpdateSubmodules;
				if (GitService.UseRebaseOptionWhenPulling)
					Rebase (RootRepository.Head.TrackedBranch.FriendlyName, options, monitor);
				else
					Merge (RootRepository.Head.TrackedBranch.FriendlyName, options, monitor);

				monitor.Step (1);
			}

			monitor.EndTask ();
		}
		void WriteProjects (SolutionFolder folder, SlnFile sln, ProgressMonitor monitor, HashSet<string> unknownProjects)
		{
			monitor.BeginTask (folder.Items.Count); 
			foreach (SolutionFolderItem ce in folder.Items.ToArray ())
			{
				monitor.BeginStep ();
				if (ce is SolutionItem) {
					
					SolutionItem item = (SolutionItem) ce;

					var proj = sln.Projects.GetOrCreateProject (ce.ItemId);
					proj.TypeGuid = item.TypeGuid;
					proj.Name = item.Name;
					proj.FilePath = FileService.NormalizeRelativePath (FileService.AbsoluteToRelativePath (sln.BaseDirectory, item.FileName)).Replace ('/', '\\');

					var sec = proj.Sections.GetOrCreateSection ("MonoDevelopProperties", SlnSectionType.PreProcess);
					sec.SkipIfEmpty = true;
					folder.ParentSolution.WriteSolutionFolderItemData (monitor, sec.Properties, ce);

					if (item.ItemDependencies.Count > 0) {
						sec = proj.Sections.GetOrCreateSection ("ProjectDependencies", SlnSectionType.PostProcess);
						sec.Properties.ClearExcept (unknownProjects);
						foreach (var dep in item.ItemDependencies)
							sec.Properties.SetValue (dep.ItemId, dep.ItemId);
					} else
						proj.Sections.RemoveSection ("ProjectDependencies");
				} else if (ce is SolutionFolder) {
					var proj = sln.Projects.GetOrCreateProject (ce.ItemId);
					proj.TypeGuid = MSBuildProjectService.FolderTypeGuid;
					proj.Name = ce.Name;
					proj.FilePath = ce.Name;

					// Folder files
					WriteFolderFiles (proj, (SolutionFolder) ce);
					
					//Write custom properties
					var sec = proj.Sections.GetOrCreateSection ("MonoDevelopProperties", SlnSectionType.PreProcess);
					sec.SkipIfEmpty = true;
					folder.ParentSolution.WriteSolutionFolderItemData (monitor, sec.Properties, ce);
				}
				if (ce is SolutionFolder)
					WriteProjects (ce as SolutionFolder, sln, monitor, unknownProjects);
			}
			monitor.EndTask ();
		}
		public void Rebase (string branch, GitUpdateOptions options, ProgressMonitor monitor)
		{
			int stashIndex = -1;
			var oldHead = RootRepository.Head.Tip;

			try {
				monitor.BeginTask (GettextCatalog.GetString ("Rebasing"), 5);
				if (!CommonPreMergeRebase (options, monitor, out stashIndex))
					return;

				// Do a rebase.
				var divergence = RootRepository.ObjectDatabase.CalculateHistoryDivergence (RootRepository.Head.Tip, RootRepository.Branches [branch].Tip);
				var toApply = RootRepository.Commits.QueryBy (new CommitFilter {
					IncludeReachableFrom = RootRepository.Head.Tip,
					ExcludeReachableFrom = divergence.CommonAncestor,
					SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Reverse
				}).ToArray ();

				RootRepository.Reset (ResetMode.Hard, divergence.Another);

				int count = toApply.Length;
				int i = 1;
				foreach (var com in toApply) {
					monitor.Log.WriteLine ("Cherry-picking {0} - {1}/{2}", com.Id, i, count);
					CherryPickResult cherryRes = RootRepository.CherryPick (com, com.Author, new CherryPickOptions {
						CheckoutNotifyFlags = refreshFlags,
						OnCheckoutNotify = RefreshFile,
					});
					if (cherryRes.Status == CherryPickStatus.Conflicts)
						ConflictResolver(monitor, toApply.Last(), RootRepository.Info.Message ?? com.Message);
					++i;
				}
			} finally {
				CommonPostMergeRebase (stashIndex, options, monitor, oldHead);
			}
		}
		internal void LoadSolution (Solution sol, SlnFile sln, ProgressMonitor monitor, SolutionLoadContext ctx)
		{
			var version = sln.FormatVersion;

			//Parse the .sln file
			var folder = sol.RootFolder;
			sol.Version = "0.1"; //FIXME:

			monitor.BeginTask("Loading projects ..", sln.Projects.Count + 1);
			Dictionary<string, SolutionFolderItem> items = new Dictionary<string, SolutionFolderItem> ();
			List<string> sortedList = new List<string> ();

			List<Task> loadTasks = new List<Task> ();

			foreach (SlnProject sec in sln.Projects) {
				try {
					// Valid guid?
					new Guid (sec.TypeGuid);
				} catch (FormatException) {
					monitor.Step (1);
					//Use default guid as projectGuid
					LoggingService.LogDebug (GettextCatalog.GetString (
						"Invalid Project type guid '{0}' on line #{1}. Ignoring.",
						sec.Id,
						sec.Line));
					continue;
				}

				string projTypeGuid = sec.TypeGuid.ToUpper ();
				string projectName = sec.Name;
				string projectPath = sec.FilePath;
				string projectGuid = sec.Id;

				lock (items)
					sortedList.Add (projectGuid);

				if (projTypeGuid == MSBuildProjectService.FolderTypeGuid) {
					//Solution folder
					SolutionFolder sfolder = new SolutionFolder ();
					sfolder.Name = projectName;
					sfolder.ItemId = projectGuid;

					DeserializeSolutionItem (monitor, sol, sfolder, sec);
					
					foreach (string f in ReadFolderFiles (sec))
						sfolder.Files.Add (MSBuildProjectService.FromMSBuildPath (Path.GetDirectoryName (sol.FileName), f));

					lock (items)
						items.Add (projectGuid, sfolder);

					monitor.Step (1);
					continue;
				}

				if (projectPath.StartsWith("http://")) {
					monitor.ReportWarning (GettextCatalog.GetString (
						"{0}({1}): Projects with non-local source (http://...) not supported. '{2}'.",
						sol.FileName, sec.Line, projectPath));
					monitor.Step (1);
					continue;
				}

				string path = MSBuildProjectService.FromMSBuildPath (Path.GetDirectoryName (sol.FileName), projectPath);
				if (String.IsNullOrEmpty (path)) {
					monitor.ReportWarning (GettextCatalog.GetString (
						"Invalid project path found in {0} : {1}", sol.FileName, projectPath));
					LoggingService.LogWarning (GettextCatalog.GetString (
						"Invalid project path found in {0} : {1}", sol.FileName, projectPath));
					monitor.Step (1);
					continue;
				}

				projectPath = Path.GetFullPath (path);
				
				SolutionItem item = null;
				Task<SolutionItem> loadTask;
				DateTime ti = DateTime.Now;

				if (sol.IsSolutionItemEnabled (projectPath)) {
					loadTask = Services.ProjectService.ReadSolutionItem (monitor, projectPath, format, projTypeGuid, projectGuid, ctx);
				} else {
					loadTask = Task.FromResult<SolutionItem> (new UnloadedSolutionItem () {
						FileName = projectPath
					});
				}

				var ft = loadTask.ContinueWith (ta => {
					try {
						item = ta.Result;
						if (item == null)
							throw new UnknownSolutionItemTypeException (projTypeGuid);
					} catch (Exception cex) {
						var e = UnwrapException (cex).First ();

						string unsupportedMessage = e.Message;

						if (e is UserException) {
							var ex = (UserException) e;
							LoggingService.LogError ("{0}: {1}", ex.Message, ex.Details);
							monitor.ReportError (string.Format ("{0}{1}{1}{2}", ex.Message, Environment.NewLine, ex.Details), null);
						} else {
							LoggingService.LogError (string.Format ("Error while trying to load the project {0}", projectPath), e);
							monitor.ReportWarning (GettextCatalog.GetString (
								"Error while trying to load the project '{0}': {1}", projectPath, e.Message));
						}

						SolutionItem uitem;
						uitem = new UnknownSolutionItem () {
							FileName = projectPath,
							LoadError = unsupportedMessage,
						};
						item = uitem;
						item.ItemId = projectGuid;
						item.TypeGuid = projTypeGuid;
					}

					item.UnresolvedProjectDependencies = ReadSolutionItemDependencies (sec);

					// Deserialize the object
					DeserializeSolutionItem (monitor, sol, item, sec);

					lock (items) {
						if (!items.ContainsKey (projectGuid)) {
							items.Add (projectGuid, item);
						} else {
							monitor.ReportError (GettextCatalog.GetString ("Invalid solution file. There are two projects with the same GUID. The project {0} will be ignored.", projectPath), null);
						}
					}
					monitor.Step (1);
				});
				loadTasks.Add (ft);
			}

			Task.WaitAll (loadTasks.ToArray ());

			sol.LoadedProjects = new HashSet<string> (items.Keys);

			var nested = sln.Sections.GetSection ("NestedProjects");
			if (nested != null)
				LoadNestedProjects (nested, items, monitor);

			// Resolve project dependencies
			foreach (var it in items.Values.OfType<SolutionItem> ()) {
				if (it.UnresolvedProjectDependencies != null) {
					foreach (var id in it.UnresolvedProjectDependencies.ToArray ()) {
						SolutionFolderItem dep;
						if (items.TryGetValue (id, out dep) && dep is SolutionItem) {
							it.UnresolvedProjectDependencies.Remove (id);
							it.ItemDependencies.Add ((SolutionItem)dep);
						}
					}
					if (it.UnresolvedProjectDependencies.Count == 0)
						it.UnresolvedProjectDependencies = null;
				}
			}

			//Add top level folders and projects to the main folder
			foreach (string id in sortedList) {
				SolutionFolderItem ce;
				if (items.TryGetValue (id, out ce) && ce.ParentFolder == null)
					folder.Items.Add (ce);
			}

			//FIXME: This can be just SolutionConfiguration also!
			LoadSolutionConfigurations (sln.SolutionConfigurationsSection, sol, monitor);

			LoadProjectConfigurationMappings (sln.ProjectConfigurationsSection, sol, items, monitor);

			foreach (var e in sln.Sections) {
				string name = e.Id;
				if (name.StartsWith ("MonoDevelopProperties.")) {
					int i = name.IndexOf ('.');
					LoadMonoDevelopConfigurationProperties (name.Substring (i+1), e, sol, monitor);
				}
			}

			monitor.EndTask ();
		}
		public void Merge (string branch, GitUpdateOptions options, ProgressMonitor monitor, FastForwardStrategy strategy = FastForwardStrategy.Default)
		{
			int stashIndex = -1;
			var oldHead = RootRepository.Head.Tip;

			Signature sig = GetSignature ();
			if (sig == null)
				return;

			try {
				monitor.BeginTask (GettextCatalog.GetString ("Merging"), 5);
				CommonPreMergeRebase (options, monitor, out stashIndex);

				// Do a merge.
				MergeResult mergeResult = RootRepository.Merge (branch, sig, new MergeOptions {
					CheckoutNotifyFlags = refreshFlags,
					OnCheckoutNotify = RefreshFile,
				});

				if (mergeResult.Status == MergeStatus.Conflicts)
					ConflictResolver (monitor, RootRepository.Head.Tip, RootRepository.Info.Message);
			} finally {
				CommonPostMergeRebase (stashIndex, GitUpdateOptions.SaveLocalChanges, monitor, oldHead);
			}
		}
		void WriteFileInternal (string file, string sourceFile, Solution solution, bool saveProjects, ProgressMonitor monitor)
		{
			if (saveProjects) {
				var items = solution.GetAllSolutionItems ().ToArray ();
				monitor.BeginTask (items.Length + 1);
				foreach (var item in items) {
					try {
						monitor.BeginStep ();
						item.SavingSolution = true;
						item.SaveAsync (monitor).Wait ();
					} finally {
						item.SavingSolution = false;
					}
				}
			} else {
				monitor.BeginTask (1);
				monitor.BeginStep ();
			}

			SlnFile sln = new SlnFile ();
			sln.FileName = file;
			if (File.Exists (sourceFile)) {
				try {
					sln.Read (sourceFile);
				} catch (Exception ex) {
					LoggingService.LogError ("Existing solution can't be updated since it can't be read", ex);
				}
			}

			sln.FormatVersion = format.SlnVersion;

			// Don't modify the product description if it already has a value
			if (string.IsNullOrEmpty (sln.ProductDescription))
				sln.ProductDescription = format.ProductDescription;

			solution.WriteSolution (monitor, sln);

			sln.Write (file);
			monitor.EndTask ();
		}
		protected override void OnRevert (FilePath[] localPaths, bool recurse, ProgressMonitor monitor)
		{
			foreach (var group in GroupByRepository (localPaths)) {
				var repository = group.Key;
				var toCheckout = new HashSet<FilePath> ();
				var toUnstage = new HashSet<FilePath> ();

				foreach (var item in group)
					if (item.IsDirectory) {
						foreach (var vi in GetDirectoryVersionInfo (item, false, recurse))
							if (!vi.IsDirectory) {
								if (vi.Status == VersionStatus.Unversioned)
									continue;
								
								if ((vi.Status & VersionStatus.ScheduledAdd) == VersionStatus.ScheduledAdd)
									toUnstage.Add (vi.LocalPath);
								else
									toCheckout.Add (vi.LocalPath);
							}
					} else {
						var vi = GetVersionInfo (item);
						if (vi.Status == VersionStatus.Unversioned)
							continue;

						if ((vi.Status & VersionStatus.ScheduledAdd) == VersionStatus.ScheduledAdd)
							toUnstage.Add (vi.LocalPath);
						else
							toCheckout.Add (vi.LocalPath);
					}

				monitor.BeginTask (GettextCatalog.GetString ("Reverting files"), 1);

				var repoFiles = repository.ToGitPath (toCheckout);
				int progress = 0;
				if (toCheckout.Any ()) {
					repository.CheckoutPaths ("HEAD", repoFiles, new CheckoutOptions {
						OnCheckoutProgress = (path, completedSteps, totalSteps) => OnCheckoutProgress (completedSteps, totalSteps, monitor, ref progress),
						CheckoutModifiers = CheckoutModifiers.Force,
						CheckoutNotifyFlags = refreshFlags,
						OnCheckoutNotify = delegate (string path, CheckoutNotifyFlags notifyFlags) {
							if ((notifyFlags & CheckoutNotifyFlags.Untracked) != 0)
								FileService.NotifyFileRemoved (repository.FromGitPath (path));
							else
								RefreshFile (path, notifyFlags);
							return true;
						}
					});
					repository.Stage (repoFiles);
				}

				if (toUnstage.Any())
					repository.Unstage (repository.ToGitPath (toUnstage).ToArray ());
				monitor.EndTask ();
			}
		}
		public void Start() {
			tracker = CreateProgressMonitor ();
			tracker.BeginTask(GetDescription(), 1);
			ThreadPool.QueueUserWorkItem (BackgroundWorker);
		}
		/// <summary>
		/// Checks that the xml in this view is well-formed.
		/// </summary>
		public static XmlDocument ValidateWellFormedness (ProgressMonitor monitor, string xml, string fileName)
		{
			monitor.BeginTask (GettextCatalog.GetString ("Validating XML..."), 1);
			bool error = false;
			XmlDocument doc = null;
			
			try {
				doc = new XmlDocument ();
				doc.LoadXml (xml);
			} catch (XmlException ex) {
				monitor.ReportError (ex.Message, ex);
				AddTask (fileName, ex.Message, ex.LinePosition, ex.LineNumber, TaskSeverity.Error);
				error = true;
			}
			
			if (error) {
				monitor.Log.WriteLine (GettextCatalog.GetString ("Validation failed."));
				TaskService.ShowErrors ();
			} else {
				monitor.Log.WriteLine (GettextCatalog.GetString ("XML is valid."));
			}
			
			monitor.EndTask ();
			return error? null: doc;
		}
		internal static void TransferFilesInternal (ProgressMonitor monitor, Project sourceProject, FilePath sourcePath, Project targetProject,
		                           FilePath targetPath, bool removeFromSource, bool copyOnlyProjectFiles)
		{
			// When transfering directories, targetPath is the directory where the source
			// directory will be transfered, including the destination directory or file name.
			// For example, if sourcePath is /a1/a2/a3 and targetPath is /b1/b2, the
			// new folder or file will be /b1/b2
			
			if (targetProject == null)
				throw new ArgumentNullException ("targetProject");

			if (!targetPath.IsChildPathOf (targetProject.BaseDirectory))
				throw new ArgumentException ("Invalid project folder: " + targetPath);

			if (sourceProject != null && !sourcePath.IsChildPathOf (sourceProject.BaseDirectory))
				throw new ArgumentException ("Invalid project folder: " + sourcePath);
				
			if (copyOnlyProjectFiles && sourceProject == null)
				throw new ArgumentException ("A source project must be specified if copyOnlyProjectFiles is True");
			
			bool sourceIsFolder = Directory.Exists (sourcePath);

			bool movingFolder = removeFromSource && sourceIsFolder && (
				!copyOnlyProjectFiles ||
				ContainsOnlyProjectFiles (sourcePath, sourceProject));

			// We need to remove all files + directories from the source project
			// but when dealing with the VCS addins we need to process only the
			// files so we do not create a 'file' in the VCS which corresponds
			// to a directory in the project and blow things up.
			List<ProjectFile> filesToRemove = null;
			List<ProjectFile> filesToMove = null;
			try {
				//get the real ProjectFiles
				if (sourceProject != null) {
					if (sourceIsFolder) {
						var virtualPath = sourcePath.ToRelative (sourceProject.BaseDirectory);
						// Grab all the child nodes of the folder we just dragged/dropped
						filesToRemove = sourceProject.Files.GetFilesInVirtualPath (virtualPath).ToList ();
						// Add the folder itself so we can remove it from the source project if its a Move operation
						var folder = sourceProject.Files.FirstOrDefault (f => f.ProjectVirtualPath == virtualPath);
						if (folder != null)
							filesToRemove.Add (folder);
					} else {
						filesToRemove = new List<ProjectFile> ();
						var pf = sourceProject.Files.GetFileWithVirtualPath (sourceProject.GetRelativeChildPath (sourcePath));
						if (pf != null)
							filesToRemove.Add (pf);
					}
				}
				//get all the non-project files and create fake ProjectFiles
				if (!copyOnlyProjectFiles || sourceProject == null) {
					var col = new List<ProjectFile> ();
					GetAllFilesRecursive (sourcePath, col);
					if (sourceProject != null) {
						var names = new HashSet<string> (filesToRemove.Select (f => sourceProject.BaseDirectory.Combine (f.ProjectVirtualPath).ToString ()));
						foreach (var f in col)
							if (names.Add (f.Name))
							    filesToRemove.Add (f);
					} else {
						filesToRemove = col;
					}
				}
			} catch (Exception ex) {
				monitor.ReportError (GettextCatalog.GetString ("Could not get any file from '{0}'.", sourcePath), ex);
				return;
			}
			
			// Strip out all the directories to leave us with just the files.
			filesToMove = filesToRemove.Where (f => f.Subtype != Subtype.Directory).ToList ();
			
			// If copying a single file, bring any grouped children along
			ProjectFile sourceParent = null;
			if (filesToMove.Count == 1 && sourceProject != null) {
				var pf = filesToMove[0];
				if (pf != null && pf.HasChildren) {
					foreach (ProjectFile child in pf.DependentChildren) {
						filesToRemove.Add (child);
						filesToMove.Add (child);
					}
				}
				sourceParent = pf;
			}
			
			// Ensure that the destination folder is created, even if no files
			// are copied
			
			try {
				if (sourceIsFolder && !Directory.Exists (targetPath) && !movingFolder)
					FileService.CreateDirectory (targetPath);
			} catch (Exception ex) {
				monitor.ReportError (GettextCatalog.GetString ("Could not create directory '{0}'.", targetPath), ex);
				return;
			}

			// Transfer files
			// If moving a folder, do it all at once
			
			if (movingFolder) {
				try {
					FileService.MoveDirectory (sourcePath, targetPath);
				} catch (Exception ex) {
					monitor.ReportError (GettextCatalog.GetString ("Directory '{0}' could not be moved.", sourcePath), ex);
					return;
				}
			}

			if (removeFromSource)
				monitor.BeginTask (GettextCatalog.GetString ("Moving files..."), filesToMove.Count);
			else
				monitor.BeginTask (GettextCatalog.GetString ("Copying files..."), filesToMove.Count);
			
			ProjectFile targetParent = null;
			foreach (ProjectFile file in filesToMove) {
				bool fileIsLink = file.Project != null && file.IsLink;
				
				var sourceFile = fileIsLink
					? file.Project.BaseDirectory.Combine (file.ProjectVirtualPath)
					: file.FilePath;
				
				FilePath newFile;
				if (sourceIsFolder)
					newFile = targetPath.Combine (sourceFile.ToRelative (sourcePath));
				else if (sourceFile == sourcePath)
					newFile = targetPath;
				else if (sourceFile.ParentDirectory != targetPath.ParentDirectory)
					newFile = targetPath.ParentDirectory.Combine (sourceFile.ToRelative (sourcePath.ParentDirectory));
				else
					newFile = GetTargetCopyName (sourceFile, false);
				
				if (!movingFolder && !fileIsLink) {
					try {
						FilePath fileDir = newFile.ParentDirectory;
						if (!Directory.Exists (fileDir) && !file.IsLink)
							FileService.CreateDirectory (fileDir);
						if (removeFromSource) {
							// File.Move() does not have an overwrite argument and will fail if the destFile path exists, however, the user
							// has already chosen to overwrite the destination file.
							if (File.Exists (newFile))
								File.Delete (newFile);

							FileService.MoveFile (sourceFile, newFile);
						} else
							FileService.CopyFile (sourceFile, newFile);
					} catch (Exception ex) {
						if (removeFromSource)
							monitor.ReportError (GettextCatalog.GetString ("File '{0}' could not be moved.", sourceFile), ex);
						else
							monitor.ReportError (GettextCatalog.GetString ("File '{0}' could not be copied.", sourceFile), ex);
						monitor.Step (1);
						continue;
					}
				}
				
				if (sourceProject != null) {
					if (fileIsLink) {
						var linkFile = (ProjectFile) file.Clone ();
						if (movingFolder) {
							var abs = linkFile.Link.ToAbsolute (sourceProject.BaseDirectory);
							var relSrc = abs.ToRelative (sourcePath);
							var absTarg = relSrc.ToAbsolute (targetPath);
							linkFile.Link = absTarg.ToRelative (targetProject.BaseDirectory);
						} else {
							linkFile.Link = newFile.ToRelative (targetProject.BaseDirectory);
						}
						targetProject.Files.Add (linkFile);
					} else if (targetProject.Files.GetFile (newFile) == null) {
						ProjectFile projectFile = (ProjectFile) file.Clone ();
						projectFile.Name = newFile;
						targetProject.Files.Add (projectFile);
						if (targetParent == null) {
							if (file == sourceParent)
								targetParent = projectFile;
						} else if (sourceParent != null) {
							if (projectFile.DependsOn == sourceParent.Name)
								projectFile.DependsOn = targetParent.Name;
						}
					}
				}
				
				monitor.Step (1);
			}
			
			if (removeFromSource) {
				// Remove all files and directories under 'sourcePath'
				foreach (var v in filesToRemove)
					sourceProject.Files.Remove (v);
			}

			// Moving or copying an empty folder. A new folder object has to be added to the project.
			if (sourceIsFolder && !targetProject.Files.GetFilesInVirtualPath (targetPath).Any ()) {
				var folderFile = new ProjectFile (targetPath) { Subtype = Subtype.Directory };
				targetProject.Files.Add (folderFile);
			}
			
			var pfolder = sourcePath.ParentDirectory;
			
			// If this was the last item in the folder, make sure we keep
			// a reference to the folder, so it is not deleted from the tree.
			if (removeFromSource && sourceProject != null && pfolder.CanonicalPath != sourceProject.BaseDirectory.CanonicalPath && pfolder.IsChildPathOf (sourceProject.BaseDirectory)) {
				pfolder = pfolder.ToRelative (sourceProject.BaseDirectory);
				if (!sourceProject.Files.GetFilesInVirtualPath (pfolder).Any () && sourceProject.Files.GetFileWithVirtualPath (pfolder) == null) {
					var folderFile = new ProjectFile (sourceProject.BaseDirectory.Combine (pfolder));
					folderFile.Subtype = Subtype.Directory;
					sourceProject.Files.Add (folderFile);
				}
			}
			
			monitor.EndTask ();
		}
		/// <summary>
		/// Validates the xml against known schemas.
		/// </summary>		
		public static XmlDocument ValidateXml (ProgressMonitor monitor, string xml, string fileName)
		{
			monitor.BeginTask (GettextCatalog.GetString ("Validating XML..."), 1);
			bool error = false;
			XmlDocument doc = null;
			StringReader stringReader = new StringReader (xml);
			
			XmlReaderSettings settings = new XmlReaderSettings ();
			settings.ValidationFlags = XmlSchemaValidationFlags.ProcessIdentityConstraints
					| XmlSchemaValidationFlags.ProcessInlineSchema
					| XmlSchemaValidationFlags.ProcessSchemaLocation
					| XmlSchemaValidationFlags.ReportValidationWarnings;
			settings.ValidationType = ValidationType.Schema;
			settings.DtdProcessing = DtdProcessing.Parse;
			
			ValidationEventHandler validationHandler = delegate (object sender, System.Xml.Schema.ValidationEventArgs args) {
				if (args.Severity == XmlSeverityType.Warning) {
					monitor.Log.WriteLine (args.Message);
					AddTask (fileName, args.Exception.Message, args.Exception.LinePosition, args.Exception.LineNumber,TaskSeverity.Warning);
				} else {
					AddTask (fileName, args.Exception.Message, args.Exception.LinePosition, args.Exception.LineNumber,TaskSeverity.Error);
					monitor.Log.WriteLine (args.Message);
					error = true;
				}	
			};
			settings.ValidationEventHandler += validationHandler;
			
			try {
				foreach (XmlSchemaCompletionData sd in XmlSchemaManager.SchemaCompletionDataItems)
					settings.Schemas.Add (sd.Schema);
				settings.Schemas.Compile ();
				
				XmlReader reader = XmlReader.Create (stringReader, settings);
				doc = new XmlDocument();
				doc.Load (reader);
				
			} catch (XmlSchemaException ex) {
				monitor.ReportError (ex.Message, ex);
				AddTask (fileName, ex.Message, ex.LinePosition, ex.LineNumber,TaskSeverity.Error);
				error = true;
			}
			catch (XmlException ex) {
				monitor.ReportError (ex.Message, ex);
				AddTask (fileName, ex.Message, ex.LinePosition, ex.LineNumber,TaskSeverity.Error);
				error = true;
			}
			finally {
				if (stringReader != null)
					stringReader.Dispose ();
				settings.ValidationEventHandler -= validationHandler;
			}
			
			if (error) {
				monitor.Log.WriteLine (GettextCatalog.GetString ("Validation failed."));
				TaskService.ShowErrors ();
			} else {
				monitor.Log.WriteLine  (GettextCatalog.GetString ("XML is valid."));
			}
			
			monitor.EndTask ();
			return error? null: doc;
		}