/// <summary>
		/// Adds the custom classes from user interface definition files.
		/// </summary>
		/// <returns>
		/// <c>true</c> if new types were added to the project, or <c>false</c> otherwise.
		/// </returns>
		/// <param name='monitor'>
		/// A progress monitor.
		/// </param>
		/// <param name='context'>
		/// A sync-back context.
		/// </param>
		bool AddCustomClassesFromUIDefinitionFiles (IProgressMonitor monitor, XcodeSyncBackContext context)
		{
			var provider = dnp.LanguageBinding.GetCodeDomProvider ();
			var options = new System.CodeDom.Compiler.CodeGeneratorOptions ();
			var writer = MonoDevelop.DesignerSupport.CodeBehindWriter.CreateForProject (
				new MonoDevelop.Core.ProgressMonitoring.NullProgressMonitor (), dnp);
			bool addedTypes = false;
			bool beganTask = false;
			
			// Collect our list of custom classes from UI definition files
			foreach (var job in context.FileSyncJobs) {
				if (!HasInterfaceDefinitionExtension (job.Original))
					continue;
				
				if (!beganTask) {
					monitor.BeginTask (GettextCatalog.GetString ("Generating custom classes defined in UI definition files..."), 0);
					beganTask = true;
				}
				
				string relative = job.SyncedRelative.ParentDirectory;
				string dir = dnp.BaseDirectory;
				
				if (!string.IsNullOrEmpty (relative))
					dir = Path.Combine (dir, relative);
				
				foreach (var type in GetCustomTypesFromUIDefinition (job.Original)) {
					if (context.ProjectInfo.ContainsType (type.ObjCName))
						continue;
					
					string designerPath = Path.Combine (dir, type.ObjCName + ".designer." + provider.FileExtension);
					string path = Path.Combine (dir, type.ObjCName + "." + provider.FileExtension);
					string ns = dnp.GetDefaultNamespace (path);
					
					type.CliName = ns + "." + provider.CreateValidIdentifier (type.ObjCName);
					
					if (provider is Microsoft.CSharp.CSharpCodeProvider) {
						CodebehindTemplateBase cs = new CSharpCodeTypeDefinition () {
							WrapperNamespace = infoService.WrapperRoot,
							Provider = provider,
							Type = type,
						};
						
						writer.WriteFile (path, cs.TransformText ());
						
						List<NSObjectTypeInfo> types = new List<NSObjectTypeInfo> ();
						types.Add (type);
						
						cs = new CSharpCodeCodebehind () {
							WrapperNamespace = infoService.WrapperRoot,
							Provider = provider,
							Types = types,
						};
						
						writer.WriteFile (designerPath, cs.TransformText ());
						
						context.ProjectInfo.InsertUpdatedType (type);
					} else {
						// FIXME: implement support for non-C# languages
					}
					
					dnp.AddFile (new ProjectFile (path));
					dnp.AddFile (new ProjectFile (designerPath) { DependsOn = path });
					addedTypes = true;
				}
			}
			
			writer.WriteOpenFiles ();
			
			if (beganTask)
				monitor.EndTask ();
			
			return addedTypes;
		}
		/// <summary>
		/// Updates the cli types.
		/// </summary>
		/// <returns>
		/// Returns whether or not any files were added to the project.
		/// </returns>
		/// <param name='monitor'>
		/// A progress monitor.
		/// </param>
		/// <param name='context'>
		/// A sync-back context.
		/// </param>
		/// <param name='typesAdded'>
		/// An output variable specifying whether or not any types were added to the project.
		/// </param>
		bool UpdateCliTypes (IProgressMonitor monitor, XcodeSyncBackContext context, out bool typesAdded)
		{
			var provider = dnp.LanguageBinding.GetCodeDomProvider ();
			var options = new System.CodeDom.Compiler.CodeGeneratorOptions ();
			var writer = MonoDevelop.DesignerSupport.CodeBehindWriter.CreateForProject (
				new MonoDevelop.Core.ProgressMonitoring.NullProgressMonitor (), dnp);
			
			monitor.BeginTask (GettextCatalog.GetString ("Detecting changes made in Xcode to custom user types..."), 0);
			Dictionary<string, NSObjectTypeInfo> newTypes;
			Dictionary<string, ProjectFile> newFiles;
			var updates = context.GetTypeUpdates (monitor, provider, out newTypes, out newFiles);
			if ((updates == null || updates.Count == 0) && newTypes == null && newFiles == null) {
				monitor.Log.WriteLine ("No changes found.");
				monitor.EndTask ();
				typesAdded = false;
				return false;
			}
			monitor.EndTask ();
			
			int count = updates.Count + (newTypes != null ? newTypes.Count : 0);
			monitor.BeginTask (GettextCatalog.GetString ("Updating custom user types in MonoDevelop..."), count);
			
			// First, add new types...
			if (newTypes != null && newTypes.Count > 0) {
				foreach (var nt in newTypes) {
					monitor.Log.WriteLine ("Adding new type: {0}", nt.Value.CliName);
					
					if (provider is Microsoft.CSharp.CSharpCodeProvider) {
						var cs = new CSharpCodeTypeDefinition () {
							WrapperNamespace = infoService.WrapperRoot,
							Provider = provider,
							Type = nt.Value,
						};
						
						string baseDir = Path.GetDirectoryName (nt.Key);
						if (!Directory.Exists (baseDir))
							Directory.CreateDirectory (baseDir);
						
						writer.WriteFile (nt.Key, cs.TransformText ());
					} else {
						// FIXME: implement support for non-C# languages
					}
					
					monitor.Step (1);
				}
				
				typesAdded = true;
			} else {
				typesAdded = false;
			}
			
			// Next, generate the designer files for any added/changed types
			foreach (var df in updates) {
				monitor.Log.WriteLine ("Generating designer file: {0}", df.Key);
				if (provider is Microsoft.CSharp.CSharpCodeProvider) {
					var cs = new CSharpCodeCodebehind () {
						Types = df.Value,
						WrapperNamespace = infoService.WrapperRoot,
						Provider = provider,
					};
					writer.WriteFile (df.Key, cs.TransformText ());
				} else {
					var ccu = GenerateCompileUnit (provider, options, df.Key, df.Value);
					writer.WriteFile (df.Key, ccu);
				}
				
				monitor.Step (1);
			}
			
			writer.WriteOpenFiles ();
			
			// Update sync timestamps
			foreach (var df in updates) {
				foreach (var type in df.Value)
					context.SetSyncTime (type.ObjCName + ".h", DateTime.Now);
			}
			
			// Add new files to the DotNetProject
			if (newFiles != null) {
				foreach (var f in newFiles) {
					monitor.Log.WriteLine ("Added new designer file: {0}", f.Key);
					dnp.AddFile (f.Value);
				}
			}
			
			monitor.EndTask ();
			
			return newFiles != null && newFiles.Count > 0;
		}
		void UpdateCliTypes (IProgressMonitor monitor, XcodeSyncBackContext context)
		{
			
			var provider = dnp.LanguageBinding.GetCodeDomProvider ();
			var options = new System.CodeDom.Compiler.CodeGeneratorOptions ();
			var writer = MonoDevelop.DesignerSupport.CodeBehindWriter.CreateForProject (
				new MonoDevelop.Core.ProgressMonitoring.NullProgressMonitor (), dnp);
			
			monitor.BeginTask (GettextCatalog.GetString ("Detecting changed types from Xcode"), 0);
			Dictionary<string,ProjectFile> newFiles;
			var updates = context.GetTypeUpdates (out newFiles);
			if (updates == null || updates.Count == 0) {
				monitor.Log.WriteLine ("No changed types found");
				monitor.EndTask ();
				return;
			}
			monitor.Log.WriteLine ("Found {0} changed types", updates.Count);
			monitor.EndTask ();
			
			monitor.BeginTask (GettextCatalog.GetString ("Updating types in MonoDevelop"), updates.Count);
			foreach (var df in updates) {
				monitor.Log.WriteLine ("Syncing {0} types from Xcode to file '{1}'", df.Value.Count, df.Key);
				if (provider is Microsoft.CSharp.CSharpCodeProvider) {
					var cs = new CSharpCodeCodebehind () {
						Types = df.Value,
						WrapperNamespace = infoService.WrapperRoot,
						Provider = provider,
					};
					writer.WriteFile (df.Key, cs.TransformText ());
				} else {
					var ccu = GenerateCompileUnit (provider, options, df.Key, df.Value);
					writer.WriteFile (df.Key, ccu);
				}
				monitor.Step (1);
			}
			writer.WriteOpenFiles ();
			
			foreach (var df in updates) {
				foreach (var type in df.Value) {
					context.SetSyncTimeToNow (type.ObjCName + ".h");
					context.SetSyncTimeToNow (type.ObjCName + ".m");
				}
			}
			
			foreach (var job in context.TypeSyncJobs) {
				context.ProjectInfo.InsertUpdatedType (job.Type);
			}
			
			if (newFiles != null) {
				foreach (var f in newFiles) {
					monitor.Log.WriteLine ("Added new designer file {0}", f.Key);
					dnp.AddFile (f.Value);
				}
				monitor.Log.WriteLine ("Saving project '{0}'", dnp.Name);
				Ide.IdeApp.ProjectOperations.Save (dnp);
			}
			monitor.EndTask ();
		}